Merge remote-tracking branch 'upstream/dev' into authnz

This commit is contained in:
Dannon Baker
2019-05-20 08:44:12 -04:00
174 changed files with 5963 additions and 3285 deletions
+3
View File
@@ -32,6 +32,9 @@ export { default as HDAModel } from "mvc/history/hda-model";
export { default as LegacyGridView } from "legacy/grid/grid-view";
export { create_chart, create_histogram } from "reports/run_stats";
export { default as ToolshedGroups } from "toolshed/toolshed.groups";
export { default as IES } from "galaxy.interactive_environments";
export { Toast } from "ui/toast"; // TODO: remove when external consumers are updated/gone (IES right now)
export function adminToolshed(options) {
new AdminToolshed.GalaxyApp(options);
@@ -153,11 +153,9 @@ describe("DataDialog.vue", () => {
emitted = wrapper.emitted();
expect(wrapper.classes()).contain("data-dialog-modal");
expect(wrapper.find(".fa-spinner").text()).to.equals("");
expect(wrapper.find(".btn-secondary").text()).to.equals("Clear");
expect(wrapper.find(".btn-primary").text()).to.equals("Ok");
expect(wrapper.contains(".fa-spinner")).to.equals(true);
return Vue.nextTick().then(() => {
expect(wrapper.findAll(".fa-copy").length).to.equals(2);
expect(wrapper.findAll(".fa-folder").length).to.equals(2);
expect(wrapper.findAll(".fa-file-o").length).to.equals(2);
});
});
@@ -169,11 +167,9 @@ describe("DataDialog.vue", () => {
emitted = wrapper.emitted();
expect(wrapper.classes()).contain("data-dialog-modal");
expect(wrapper.find(".fa-spinner").text()).to.equals("");
expect(wrapper.find(".btn-secondary").text()).to.equals("Clear");
expect(wrapper.find(".btn-primary").text()).to.equals("Ok");
expect(wrapper.contains(".fa-spinner")).to.equals(true);
return Vue.nextTick().then(() => {
expect(wrapper.findAll(".fa-copy").length).to.equals(2);
expect(wrapper.findAll(".fa-folder").length).to.equals(2);
expect(wrapper.findAll(".fa-file-o").length).to.equals(2);
});
});
@@ -20,7 +20,14 @@
<div class="fa fa-caret-left mr-1" />
Back
</b-btn>
<b-btn size="sm" class="float-right ml-1" variant="primary" @click="finalize" :disabled="!hasValue">
<b-btn
v-if="multiple"
size="sm"
class="float-right ml-1"
variant="primary"
@click="finalize"
:disabled="!hasValue"
>
Ok
</b-btn>
<b-btn size="sm" class="float-right" @click="modalShow = false"> Cancel </b-btn>
@@ -117,6 +124,8 @@ export default {
} else {
this.finalize();
}
} else {
this.load(record.url);
}
},
/** Called when selection is complete, values are formatted and parsed to external callback **/
@@ -2,7 +2,7 @@
<b-input-group>
<b-input v-model="filter" placeholder="Type to Search" />
<b-input-group-append>
<b-btn :disabled="!filter" @click="filter = ''">Clear</b-btn>
<b-btn :disabled="!filter" @click="filter = ''"><i class="fa fa-times"/></b-btn>
</b-input-group-append>
</b-input-group>
</template>
@@ -12,7 +12,7 @@
@filtered="filtered"
>
<template slot="label" slot-scope="data">
<i v-if="data.item.isDataset" class="fa fa-file-o" /> <i v-else class="fa fa-copy" />
<i v-if="data.item.isDataset" class="fa fa-file-o" /> <i v-else class="fa fa-folder" />
{{ data.value ? data.value : "-" }}
</template>
<template slot="details" slot-scope="data">
@@ -21,17 +21,6 @@
<template slot="time" slot-scope="data">
{{ data.value ? data.value : "-" }}
</template>
<template slot="arrow" slot-scope="data">
<b-button
variant="link"
size="sm"
class="py-0"
v-if="!data.item.isDataset"
@click.stop="load(data.item.url)"
>
View
</b-button>
</template>
</b-table>
<div v-if="nItems === 0">
<div v-if="filter">
@@ -77,11 +66,6 @@ export default {
},
time: {
sortable: true
},
arrow: {
label: "",
sortable: false,
class: "text-right"
}
},
nItems: 0,
@@ -105,10 +89,6 @@ export default {
/** Collects selected datasets in value array **/
clicked: function(record) {
this.$emit("clicked", record);
},
/** Performs server request to retrieve data records **/
load: function(url) {
this.$emit("load", url);
}
}
};
+7 -6
View File
@@ -25,7 +25,7 @@
<span id="item-url-text" style="display: none">
{{ item_url_parts[0] }}<span id="item-identifier">{{ item_url_parts[1] }}</span>
</span>
<a href="#" id="edit-identifier"><img :src="pencil_url"/></a>
<a href="javascript:void(0)" id="edit-identifier"><img :src="pencil_url" alt="Edit Share Url"/></a>
</blockquote>
<div v-if="item.published">
<p>
@@ -170,7 +170,8 @@ export default {
return this.item.published ? "accessible via link and published" : "accessible via link";
},
item_url() {
return `${window.location.protocol}//${window.location.hostname}:${window.location.port}${getAppRoot()}${
const port = window.location.port ? `:${window.location.port}` : "";
return `${window.location.protocol}//${window.location.hostname}${port}${getAppRoot()}${
this.item.username_and_slug
}`;
},
@@ -263,7 +264,7 @@ export default {
.catch(error => (this.err_msg = error.response.data.err_msg));
},
createSlugHandler: function() {
var on_start = function(text_elt) {
const on_start = function(text_elt) {
// Replace URL with URL text.
$("#item-url").hide();
$("#item-url-text").show();
@@ -279,14 +280,14 @@ export default {
);
});
};
var on_finish = function(text_elt) {
const on_finish = function(text_elt) {
// Replace URL text with URL.
$("#item-url-text").hide();
$("#item-url").show();
// Set URL to new value.
var new_url = $("#item-url-text").text();
var item_url_obj = $("#item-url");
const new_url = $("#item-url-text").text();
const item_url_obj = $("#item-url");
item_url_obj.attr("href", new_url);
item_url_obj.text(new_url);
};
@@ -43,8 +43,6 @@ const makoClickHandler = (options, vm) =>
const { tagClickFn = "none", clickUrl } = options;
switch (tagClickFn) {
// I made this match the existing behavior, but I am not clear on
// the reason why this link redirects to a raw json page
case "community_tag_click":
if (undefined !== clickUrl) {
const suffix = tag.value ? `:${tag.value}` : "";
@@ -0,0 +1,370 @@
<template>
<div>
<h2 class="mb-3">
<span id="jobs-title">Jobs</span>
</h2>
<b-alert v-if="this.message !== ''" :variant="galaxyKwdToBootstrap(status)" show>
{{ message }}
</b-alert>
<b-alert variant="info" show>
<p>
Unfinished and recently finished jobs are displayed on this page. The 'cutoff' input box will do two
things -- it will limit the display of unfinished jobs to only those jobs that have not had their job
state updated recently, and it will limit the recently finished jobs list to only displaying jobs that
have finished since the cutoff.
</p>
If any jobs are displayed, you may choose to stop them. Your stop message will be displayed to the user as:
"This job was stopped by an administrator: <strong>&lt;YOUR MESSAGE&gt;</strong> For more information or
help, report this error".
</b-alert>
<b-alert v-if="loading" variant="info" show>
Waiting for data
</b-alert>
<div v-else>
<b-container class="mb-3">
<b-row>
<b-col md="6">
<b-form name="jobs" @submit.prevent="onRefresh">
<b-form-group
id="cutoff"
label="Update Jobs"
label-for="cutoff-seconds"
description="Cutoff in seconds"
>
<b-input-group>
<b-form-input id="cutoff" type="number" placeholder="180" v-model="cutoff">
</b-form-input>
<b-input-group-append>
<b-btn type="submit">Refresh</b-btn>
</b-input-group-append>
</b-input-group>
</b-form-group>
</b-form>
</b-col>
<b-col>
<b-form-group label="Administrative Job Lock" label-for="prevent-job-dispatching">
<b-form-checkbox id="prevent-job-dispatching" v-model="jobLock" switch>
Job dispatching is currently
<strong>{{ jobLockDisplay ? "locked" : "unlocked" }}</strong>
</b-form-checkbox>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col md="6">
<b-form-group
label="Filter Jobs"
label-for="filter-regex"
description="Search for strings or regular expressions"
>
<b-input-group id="filter-regex">
<b-form-input
v-model="filter"
placeholder="Type to Search"
@keyup.esc.native="filter = ''"
/>
<b-input-group-append>
<b-btn :disabled="!filter" @click="filter = ''">Clear (esc)</b-btn>
</b-input-group-append>
</b-input-group>
</b-form-group>
<b-button :pressed.sync="showCommandLine" variant="outline-secondary">
{{ showCommandLine ? "Hide" : "Show" }} Command Line
</b-button>
</b-col>
<b-col>
<b-card v-if="jobsItemsComputed.length" header="Stop Selected Jobs">
<b-form @submit.prevent="onStopJobs">
<b-form-group label-for="stop-message" description="will be displayed to the user">
<b-input-group>
<b-form-input
id="stop-message"
v-model="stopMessage"
placeholder="Stop message"
required
>
</b-form-input>
<b-input-group-append>
<b-btn type="submit">Submit</b-btn>
</b-input-group-append>
</b-input-group>
</b-form-group>
</b-form>
</b-card>
</b-col>
</b-row>
</b-container>
<b-alert v-if="!jobsItemsComputed.length" variant="secondary" show>
There are no unfinished jobs to show with current cutoff time.
</b-alert>
<b-table
v-else
:fields="jobsFieldsComputed"
:items="jobsItemsComputed"
v-model="jobsItemsModel"
:filter="filter"
hover
responsive
striped
caption-top
@row-clicked="showRowDetails"
:busy="busy"
>
<template slot="table-caption">
Unfinished Jobs: These jobs are unfinished and have had their state updated in the previous
{{ cutoffDisplay }} seconds.
</template>
<!-- Enable cell formatting for the command line column -->
<span slot="html" slot-scope="data" v-html="data.value" />
<template slot="HEAD_selected">
<b-form-checkbox
v-model="allSelected"
:indeterminate="indeterminate"
@change="toggleAll"
></b-form-checkbox>
</template>
<template slot="selected" slot-scope="data">
<b-form-checkbox
v-model="selectedStopJobIds"
:checked="allSelected"
:key="data.index"
:value="data.item['job_info']['id']"
></b-form-checkbox>
</template>
<template slot="job_info" slot-scope="data">
<b-link :href="data.value['info_url']" target="galaxy_main">
{{ data.value["id"] }}
</b-link>
</template>
<template slot="row-details" slot-scope="row">
<b-card>
<h5>Command Line</h5>
<pre
class="text-white bg-dark"
><code class="break-word">{{ row.item.command_line }}</code></pre>
</b-card>
</template>
</b-table>
<b-alert v-if="!recentJobsItemsComputed.length" variant="secondary" show>
There are no recently finished jobs to show with current cutoff time.
</b-alert>
<b-table
v-else
:fields="recentJobsFieldsComputed"
:items="recentJobsItemsComputed"
:filter="filter"
hover
responsive
striped
caption-top
@row-clicked="showRowDetails"
:busy="busy"
>
<template slot="table-caption">
Recent Jobs: These jobs have completed in the previous {{ cutoffDisplay }} seconds.
</template>
<!-- Enable cell formatting for the command line column -->
<span slot="html" slot-scope="data" v-html="data.value" />
<template slot="job_info" slot-scope="data">
<b-link :href="data.value['info_url']" target="galaxy_main">
{{ data.value["id"] }}
</b-link>
</template>
<template slot="row-details" slot-scope="row">
<b-card>
<h5>Command Line</h5>
<pre
class="text-white bg-dark"
><code class="break-word">{{ row.item.command_line }}</code></pre>
</b-card>
</template>
</b-table>
</div>
</div>
</template>
<script>
import { getAppRoot } from "onload/loadConfig";
import axios from "axios";
export default {
data() {
return {
jobsItems: [],
jobsItemsModel: [],
recentJobsItems: [],
jobsFields: [
{ key: "job_info", label: "Job ID", sortable: true },
{ key: "user" },
{ key: "tool_id", label: "Tool", tdClass: ["break-word"] },
{ key: "state" },
{ key: "input_dataset", label: "Inputs" },
{ key: "job_runner_name", label: "Job Runner" },
{ key: "job_runner_external_id", label: "PID/Cluster ID", sortable: true }
],
showCommandLine: false,
cutoff: 180,
cutoffDisplay: 180,
jobLock: false,
jobLockDisplay: false,
selectedStopJobIds: [],
allSelected: false,
indeterminate: false,
stopMessage: "",
filter: "",
message: "",
status: "",
loading: true,
busy: true
};
},
watch: {
jobLock(newVal) {
axios
.get(`${getAppRoot()}admin/jobs_control?job_lock=${this.jobLock}`)
.then(response => {
this.jobLock = response.data.job_lock;
this.jobLockDisplay = response.data.job_lock;
})
.catch(error => {
console.error(error);
});
},
selectedStopJobIds(newVal) {
if (newVal.length === 0) {
this.indeterminate = false;
this.allSelected = false;
} else if (newVal.length === this.jobsItemsModel.length) {
this.indeterminate = false;
this.allSelected = true;
} else {
this.indeterminate = true;
this.allSelected = false;
}
}
},
methods: {
update() {
this.busy = true;
let params = [];
params.push(`cutoff=${this.cutoff}`);
params.push(`stop_msg=${this.stopMessage}`);
params.push(`stop=${this.selectedStopJobIds.join()}`);
params = params.join("&");
axios
.get(`${getAppRoot()}admin/jobs_list?${params}`)
.then(response => {
this.jobsItems = response.data.jobs;
this.recentJobsItems = response.data.recent_jobs;
this.cutoffDisplay = response.data.cutoff;
this.message = response.data.message;
this.status = response.data.status;
this.jobLock = response.data.job_lock;
this.jobLockDisplay = response.data.job_lock;
this.loading = false;
this.busy = false;
})
.catch(error => {
this.message = error.response.data.err_msg;
this.status = "error";
console.log(error.response);
});
},
onRefresh() {
this.update();
},
onStopJobs() {
this.update();
this.selectedStopJobIds = [];
this.stopMessage = "";
},
showRowDetails(row, index, e) {
if (e.target.nodeName != "A") {
row._showDetails = !row._showDetails;
}
},
translateState(state) {
const translateDict = {
ok: "success",
error: "danger",
new: "primary",
queued: "secondary",
running: "info",
upload: "dark"
};
return translateDict[state] || "primary";
},
computeItems(items) {
return items.map(job => {
return {
...job,
_showDetails: false,
_cellVariants: { state: this.translateState(job.state) }
};
});
},
computeFields(fields) {
const f = Array.from(fields).slice(0);
if (this.showCommandLine) {
f.splice(6, 0, {
key: "command_line",
tdClass: ["text-white", "bg-dark", "break-word"]
});
}
return f;
},
toggleAll(checked) {
this.selectedStopJobIds = checked
? this.jobsItemsModel.reduce((acc, j) => [...acc, j["job_info"]["id"]], [])
: [];
},
galaxyKwdToBootstrap(status) {
let variant = "info";
if (status !== "") {
variant = status;
}
const galaxyKwdToBoostrapDict = {
done: "success",
info: "info",
warning: "warning",
error: "danger"
};
if (variant in galaxyKwdToBoostrapDict) {
return galaxyKwdToBoostrapDict[variant];
} else {
return variant;
}
}
},
computed: {
jobsItemsComputed() {
return this.computeItems(this.jobsItems);
},
recentJobsItemsComputed() {
return this.computeItems(this.recentJobsItems);
},
jobsFieldsComputed() {
const f = this.jobsFields.slice(0);
f.splice(0, 0, { key: "selected", label: "" });
f.splice(2, 0, { key: "update_time", label: "Last Update", sortable: true });
return this.computeFields(f);
},
recentJobsFieldsComputed() {
const f = this.jobsFields.slice(0);
f.splice(2, 0, { key: "update_time", label: "Finished", sortable: true });
return this.computeFields(f);
}
},
created() {
this.update();
}
};
</script>
<style>
/* Can not be scoped because of command line tdClass */
.break-word {
white-space: pre-wrap;
word-break: break-word;
}
</style>
@@ -7,6 +7,7 @@ import QueryStringParsing from "utils/query-string-parsing";
import Router from "layout/router";
import DataTables from "components/admin/DataTables.vue";
import DataTypes from "components/admin/DataTypes.vue";
import Jobs from "components/admin/Jobs.vue";
import DataManagerView from "components/admin/DataManager/DataManagerView.vue";
import DataManagerRouter from "components/admin/DataManager/DataManagerRouter.vue";
import Register from "components/login/Register.vue";
@@ -33,6 +34,7 @@ export const getAdminRouter = (Galaxy, options) => {
"(/)admin(/)form(/)(:form_id)": "show_form",
"(/)admin/data_tables": "show_data_tables",
"(/)admin/data_types": "show_data_types",
"(/)admin/jobs": "show_jobs",
"(/)admin/data_manager*path": "show_data_manager",
"*notFound": "not_found"
},
@@ -115,6 +117,10 @@ export const getAdminRouter = (Galaxy, options) => {
this._display_vue_helper(DataTypes);
},
show_jobs: function() {
this._display_vue_helper(Jobs);
},
show_error_stack: function() {
this._display_vue_helper(ErrorStack);
},
@@ -39,6 +39,7 @@ var AdminPanel = Backbone.View.extend({
{
title: _l("Manage jobs"),
url: "admin/jobs",
target: "__use_router__",
id: "admin-link-jobs"
},
{
@@ -1,6 +1,6 @@
/* global $ */
/* global toastr */
// TODO: this file is transpiled and used directly without bundling; fix imports when that is no longer the case.
import $ from "jquery";
import { Toast } from "ui/toast";
/**
* Internal function to remove content from the main area and add the notebook.
* Not idempotent
@@ -35,7 +35,7 @@ export function make_spin_state(
sleep_step,
log_attempts
) {
var s = {
return {
type: typeof type !== "undefined" ? type : "GIE spin",
ajax_timeout: typeof ajax_timeout_init !== "undefined" ? ajax_timeout_init : 2000,
ajax_timeout_max: typeof ajax_timeout_max !== "undefined" ? ajax_timeout_max : 16000,
@@ -46,7 +46,6 @@ export function make_spin_state(
log_attempts: typeof log_attempts !== "undefined" ? log_attempts : true,
count: 0
};
return s;
}
/* Log/display an error when spinning fails. */
@@ -54,8 +53,8 @@ export function spin_error(console_msg, user_msg, clear) {
console.log(console_msg);
if (clear) clear_main_area();
if (typeof user_msg == "string") {
toastr.clear();
toastr.error(user_msg, "Error", {
Toast.clear();
Toast.error(user_msg, "Error", {
closeButton: true,
timeOut: 0,
extendedTimeOut: 0,
@@ -112,7 +111,7 @@ export function spin(url, bool_response, success_callback, timeout_callback, err
}
}
};
if (bool_response) ajax_params["dataType"] = "json";
if (bool_response) ajax_params.dataType = "json";
$.ajax(ajax_params);
};
console.log(`Setting up new spinner for ${spin_state.type} on ${url}`);
@@ -131,7 +130,7 @@ function spin_until(url, bool_response, messages, success_callback, spin_state)
var message_once = (message, spin_state) => {
if (spin_state.count == 1) {
display_spinner();
toastr.info(message, null, {
Toast.info(message, null, {
closeButton: true,
timeOut: 0,
extendedTimeOut: 0,
@@ -141,22 +140,22 @@ function spin_until(url, bool_response, messages, success_callback, spin_state)
};
var wrapped_success = data => {
if (!bool_response || (bool_response && data == true)) {
console.log(messages["success"]);
console.log(messages.success);
clear_main_area();
toastr.clear();
Toast.clear();
success_callback();
} else if (bool_response && data == false) {
message_once(messages["not_ready"], spin_state);
message_once(messages.not_ready, spin_state);
return false; // keep spinning
} else {
spin_error(`Invalid response to ${spin_state.type} request`, messages["invalid_response"], true);
spin_error(`Invalid response to ${spin_state.type} request`, messages.invalid_response, true);
}
return true; // stop spinning
};
var timeout_error = (jqxhr, status, error) => {
message_once(messages["waiting"], spin_state);
message_once(messages.waiting, spin_state);
if (spin_state.count == warn_at) {
toastr.warning(messages["wait_warn"], "Warning", {
Toast.warning(messages.wait_warn, "Warning", {
closeButton: true,
timeOut: 0,
extendedTimeOut: 0,
@@ -235,7 +234,7 @@ export function keepAlive(notebookAccessURL) {
if (request_count > 30) {
window.clearInterval(interval);
clear_main_area();
toastr.error("Could not connect to IE, contact your administrator", "Error", {
Toast.error("Could not connect to IE, contact your administrator", "Error", {
closeButton: true,
timeOut: 20000,
tapToDismiss: false
@@ -245,3 +244,15 @@ export function keepAlive(notebookAccessURL) {
});
}, 10000);
}
export default {
append_notebook,
clear_main_area,
display_spinner,
make_spin_state,
spin_error,
spin,
test_ie_availability,
load_when_ready,
keepAlive
};
@@ -442,7 +442,8 @@ export var HistoryContents = _super.extend(BASE_MVC.LoggableMixin).extend({
data: JSON.stringify({
content: id,
source: contentType,
type: type
type: type,
copy_elements: true
})
})
.done(response => {
File diff suppressed because it is too large Load Diff
@@ -202,56 +202,58 @@ var FolderView = Backbone.View.extend({
templateFolderPermissions: function() {
return _.template(
[
'<div class="library_style_container">',
"<div>",
'<a href="#/folders/<%= folder.get("parent_id") %>">',
'<button data-toggle="tooltip" data-placement="top" title="Go back to the parent folder" class="btn btn-secondary primary-button" type="button">',
'<span class="fa fa-caret-left fa-lg"/>',
"&nbsp;Parent folder",
"</button>",
"</a>",
"</div>",
"<h1>",
'Folder: <%= _.escape(folder.get("name")) %>',
"</h1>",
'<div class="alert alert-warning">',
"<% if (is_admin) { %>",
"You are logged in as an <strong>administrator</strong> therefore you can manage any folder on this Galaxy instance. Please make sure you understand the consequences.",
"<% } else { %>",
"You can assign any number of roles to any of the following permission types. However please read carefully the implications of such actions.",
"<% }%>",
"</div>",
'<div class="dataset_table">',
"<h2>Folder permissions</h2>",
"<h4>",
"Roles that can manage permissions on this folder",
"</h4>",
'<div id="manage_perm" class="manage_perm roles-selection"/>',
'<div class="alert alert-info roles-selection">',
"User with <strong>any</strong> of these roles can manage permissions on this folder.",
"</div>",
"<h4>",
"Roles that can add items to this folder",
"</h4>",
'<div id="add_perm" class="add_perm roles-selection"/>',
'<div class="alert alert-info roles-selection">',
"User with <strong>any</strong> of these roles can add items to this folder (folders and datasets).",
"</div>",
"<h4>",
"Roles that can modify this folder",
"</h4>",
'<div id="modify_perm" class="modify_perm roles-selection"/>',
'<div class="alert alert-info roles-selection">',
"User with <strong>any</strong> of these roles can modify this folder (name, etc.).",
"</div>",
'<button data-toggle="tooltip" data-placement="top" title="Save modifications" class="btn btn-secondary toolbtn_save_permissions primary-button" type="button">',
'<span class="fa fa-floppy-o"/>',
"&nbsp;Save",
"</button>",
"</div>",
"</div>"
].join("")
`<div class="library_style_container">
<div>
<a href="#/folders/<%= folder.get("parent_id") %>">
<button data-toggle="tooltip" data-placement="top" title="Go back to the parent folder"
class="btn btn-secondary primary-button" type="button">
<span class="fa fa-caret-left fa-lg"/>
&nbsp;Parent folder
</button>
</a>
</div>
<h1>
Folder: <%= _.escape(folder.get("name")) %>
</h1>
<div class="alert alert-warning">
<% if (is_admin) { %>
You are logged in as an <strong>administrator</strong> therefore you can manage any
folder on this Galaxy instance. Please make sure you understand the consequences.
<% } else { %>
You can assign any number of roles to any of the following permission types.
However please read carefully the implications of such actions.
<% }%>
</div>
<div class="dataset_table">
<h2>Folder permissions</h2>
<h4>
Roles that can manage permissions on this folder
</h4>
<div id="manage_perm" class="manage_perm roles-selection"></div>
<div class="alert alert-info roles-selection">
User with <strong>any</strong> of these roles can manage permissions on this folder.
</div>
<h4>
Roles that can add items to this folder
</h4>
<div id="add_perm" class="add_perm roles-selection"></div>
<div class="alert alert-info roles-selection">
User with <strong>any</strong> of these roles can add items to this folder (folders and datasets).
</div>
<h4>
Roles that can modify this folder
</h4>
<div id="modify_perm" class="modify_perm roles-selection"></div>
<div class="alert alert-info roles-selection">
User with <strong>any</strong> of these roles can modify this folder (name, etc.).
</div>
<button data-toggle="tooltip" data-placement="top" title="Save modifications"
class="btn btn-secondary toolbtn_save_permissions primary-button" type="button">
<span class="fa fa-floppy-o"/>
&nbsp;Save
</button>
</div>
</div>`
);
}
});
@@ -329,29 +329,11 @@ var FolderListView = Backbone.View.extend({
},
makeDarkRow: function($row) {
$row.removeClass("light").addClass("dark");
$row.find("a")
.removeClass("light")
.addClass("dark");
$row.find(".fa-file-o")
.removeClass("fa-file-o")
.addClass("fa-file");
$row.find(".fa-folder-o")
.removeClass("fa-folder-o")
.addClass("fa-folder");
$row.addClass("table-primary");
},
makeWhiteRow: function($row) {
$row.removeClass("dark").addClass("light");
$row.find("a")
.removeClass("dark")
.addClass("light");
$row.find(".fa-file")
.removeClass("fa-file")
.addClass("fa-file-o");
$row.find(".fa-folder")
.removeClass("fa-folder")
.addClass("fa-folder-o");
$row.removeClass("table-primary");
},
renderSortIcon: function() {
@@ -365,54 +347,186 @@ var FolderListView = Backbone.View.extend({
}
},
/**
* Create the new folder inline
*/
createFolderInline: function() {
if (this.$el.find("tr.new-row").length) {
this.$el.find("tr.new-row textarea")[0].focus();
} else {
const template = this.templateNewFolder();
this.$el.find("#first_folder_item").after(template);
this.$el.find("tr.new-row textarea")[0].focus();
this.$el.find("tr.new-row .save_folder_btn").click(() => {
this.createNewFolder(
this.$el.find("tr.new-row textarea")[0].value,
this.$el.find("tr.new-row textarea")[1].value
);
});
this.$el.find("tr.new-row .cancel_folder_btn").click(() => {
this.$el.find("tr.new-row").remove();
});
}
},
/**
* Create the new library using the API asynchronously.
*/
createNewFolder: function(name, description) {
const Galaxy = getGalaxyInstance();
const folderDetails = {
name,
description
};
if (folderDetails.name !== "") {
var folder = new mod_library_model.FolderAsModel();
var url_items = Backbone.history.fragment.split("/");
var current_folder_id;
if (url_items.indexOf("page") > -1) {
current_folder_id = url_items[url_items.length - 3];
} else {
current_folder_id = url_items[url_items.length - 1];
}
folder.url = folder.urlRoot + current_folder_id;
folder.save(folderDetails, {
success: folder => {
Toast.success("Folder created.");
folder.set({ type: "folder" });
this.$el.find("tr.new-row").remove();
Galaxy.libraries.folderListView.collection.add(folder);
$(`tr[data-id="${folder.attributes.id}"`)
.addClass("table-success")
.on("mouseover click", function() {
$(this).removeClass("table-success");
});
},
error: (model, response) => {
Galaxy.modal.hide();
if (typeof response.responseJSON !== "undefined") {
Toast.error(response.responseJSON.err_msg);
} else {
Toast.error("An error occurred.");
}
}
});
} else {
Toast.error("Folder's name is missing.");
}
return false;
},
templateNewFolder: function() {
return _.template(
`<tr class="new-row">
<td class="mid">
<span title="Folder" class="fa fa-folder-o"></span>
</td>
<td></td>
<td>
<textarea name="input_folder_name" rows="4" class="form-control input_folder_name" placeholder="name" ></textarea>
</td>
<td>
<textarea rows="4" class="form-control input_folder_description" placeholder="description" ></textarea>
</td>
<td>folder</td>
<td></td>
<td></td>
<td></td>
<td class="right-center">
<button data-toggle="tooltip" data-placement="left" title="Save changes"
class="btn btn-secondary btn-sm save_folder_btn" type="button">
<span class="fa fa-floppy-o"></span> Save
</button>
<button data-toggle="tooltip" data-placement="left" title="Discard changes"
class="btn btn-secondary btn-sm cancel_folder_btn" type="button">
<span class="fa fa-times"></span> Cancel
</button>
</td>
</tr>`
);
},
templateFolder: function() {
return _.template(
[
'<ol class="breadcrumb">',
'<li class="breadcrumb-item"><a title="Return to the list of libraries" href="#">Libraries</a></li>',
"<% _.each(path, function(path_item) { %>",
"<% if (path_item[0] != id) { %>",
'<li class="breadcrumb-item"><a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> </li> ',
"<% } else { %>",
'<li class="breadcrumb-item active"><span title="You are in this folder"><%- path_item[1] %></span></li>',
"<% } %>",
"<% }); %>",
"</ol>",
`<ol class="breadcrumb">
<li class="breadcrumb-item">
<a title="Return to the list of libraries" href="#">Libraries</a>
</li>
<% _.each(path, function(path_item) { %>
<% if (path_item[0] != id) { %>
<li class="breadcrumb-item">
<a title="Return to this folder" href="#/folders/<%- path_item[0] %>">
<%- path_item[1] %>
</a>
</li>
<% } else { %>
<li class="breadcrumb-item active">
<span title="You are in this folder">
<%- path_item[1] %>
</span>
</li>
<% } %>
<% }); %>
</ol>
// FOLDER CONTENT
'<table data-library-id="<%- parent_library_id %>" class="grid table table-sm">',
"<thead>",
'<th class="button_heading"></th>',
'<th class="mid" style="width: 20px; " title="Check to select all datasets"><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>',
'<th><a class="sort-folder-name" title="Click to reverse order" href="#">name</a> <span title="Sorted alphabetically" class="sort-icon-name fa fa-sort-alpha-<%- order %>"></span></th>',
'<th style="width:20%;"><a class="sort-folder-description" title="Click to reverse order" href="#">description</a> <span title="Sorted alphabetically" class="sort-icon-description fa"></span></th>',
'<th style="width:5%;"><a class="sort-folder-file_ext" title="Click to reverse order" href="#">data type</a> <span title="Sorted alphabetically" class="sort-icon-file_ext fa"></span></th>',
'<th style="width:10%;">size</th>',
'<th style="width:160px;">time updated (UTC)</th>',
'<th style="width:5%;"><a class="sort-folder-state" title="Click to reverse order" href="#">state</a> <span title="Sorted alphabetically" class="sort-icon-state fa"></span></th>',
'<th style="width:150px;"></th> ',
"</thead>",
'<tbody id="folder_list_body">',
'<tr id="first_folder_item">',
"<td>",
'<a href="#<% if (upper_folder_id !== 0){ print("folders/" + upper_folder_id)} %>" title="Go to parent folder" class="btn_open_folder btn btn-secondary btn-sm">..<a>',
"</td>",
"<td></td>",
"<td></td>",
"<td></td>",
"<td></td>",
"<td></td>",
"<td></td>",
"<td></td>",
"<td></td>",
"</tr>",
"</tbody>",
"</table>",
'<div class="empty-folder-message" style="display:none;">',
"This folder is either empty or you do not have proper access permissions to see the contents. If you expected something to show up",
' please consult the <a href="https://galaxyproject.org/data-libraries/#permissions" target="_blank">library security wikipage</a>.',
"</div>"
].join("")
<!-- FOLDER CONTENT -->
<table data-library-id="<%- parent_library_id %>" class="grid table table-hover table-sm">
<thead>
<th class="button_heading"></th>
<th class="mid" style="width: 20px;"
title="Check to select all datasets">
<input id="select-all-checkboxes" style="margin: 0;" type="checkbox">
</th>
<th>
<a class="sort-folder-name" title="Click to reverse order" href="#">Name</a>
<span title="Sorted alphabetically"
class="sort-icon-name fa fa-sort-alpha-<%- order %>"></span>
</th>
<th style="width:20%;">
<a class="sort-folder-description" title="Click to reverse order" href="#">Description</a>
<span title="Sorted alphabetically" class="sort-icon-description fa"></span>
</th>
<th style="width:5%;">
<a class="sort-folder-file_ext" title="Click to reverse order" href="#">Data Type</a>
<span title="Sorted alphabetically" class="sort-icon-file_ext fa"></span>
</th>
<th style="width:10%;">Size</th>
<th style="width:160px;">Time Updated (UTC)</th>
<th style="width:5%;">
<a class="sort-folder-state" title="Click to reverse order" href="#">State</a>
<span title="Sorted alphabetically" class="sort-icon-state fa"></span>
</th>
<th style="width:160px;"></th>
</thead>
<tbody id="folder_list_body">
<tr id="first_folder_item">
<td>
<a href="#<% if (upper_folder_id !== 0){ print("folders/" + upper_folder_id)} %>"
title="Go to parent folder" class="btn_open_folder btn btn-secondary btn-sm">..<a>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<div class="empty-folder-message" style="display:none;">
This folder is either empty or you do not have proper access permissions to see the contents.
If you expected something to show up please consult the
<a href="https://galaxyproject.org/data-libraries/#permissions" target="_blank">
library security wikipage
</a>.
</div>`
);
}
});
@@ -241,169 +241,205 @@ var FolderRowView = Backbone.View.extend({
templateRowFolder: function() {
return _.template(
[
'<tr class="folder_row light library-row" data-id="<%- content_item.id %>">',
'<td class="mid">',
'<span title="Folder" class="fa fa-folder-o"/>',
"</td>",
'<td class="mid"><input style="margin: 0;" type="checkbox"></td>',
"<% if(!edit_mode) { %>",
"<td>",
'<a href="#folders/<%- content_item.id %>"><%- content_item.get("name") %></a>',
"</td>",
"<td>",
'<%- content_item.get("description") %>',
"</td>",
"<% } else if(edit_mode){ %>",
'<td><textarea rows="4" class="form-control input_folder_name" placeholder="name" ><%- content_item.get("name") %></textarea></td>',
'<td><textarea rows="4" class="form-control input_folder_description" placeholder="description" ><%- content_item.get("description") %></textarea></td>',
"<% } %>",
"<td>folder</td>",
"<td></td>",
"<td>",
'<%= _.escape(content_item.get("update_time")) %>',
"</td>",
"<td></td>",
"<td>",
"<% if(edit_mode) { %>", // start edit mode
'<button data-toggle="tooltip" data-placement="top" title="Save changes" class="primary-button btn-sm save_folder_btn" type="button" style="<% if(button_config.save_folder_btn === false) { print("display:none;") } %>">',
'<span class="fa fa-floppy-o"/> Save',
"</button>",
'<button data-toggle="tooltip" data-placement="top" title="Discard changes" class="primary-button btn-sm cancel_folder_btn" type="button" style="<% if(button_config.cancel_folder_btn === false) { print("display:none;") } %>">',
'<span class="fa fa-times"/> Cancel',
"</button>",
"<% } else if (!edit_mode){%>", // start no edit mode
'<button data-toggle="tooltip" data-placement="top" title="Modify \'<%- content_item.get("name") %>\'" class="primary-button btn-sm edit_folder_btn" type="button" style="<% if(button_config.edit_folder_btn === false) { print("display:none;") } %>">',
'<span class="fa fa-pencil"/> Edit',
"</button>",
'<a href="#/folders/<%- content_item.id %>/permissions">',
'<button data-toggle="tooltip" data-placement="top" class="primary-button btn-sm permission_folder_btn" title="Permissions of \'<%- content_item.get("name") %>\'" style="<% if(button_config.permission_folder_btn === false) { print("display:none;") } %>">',
'<span class="fa fa-group"/> Manage',
"</button>",
"</a>",
"<% } %>", //end no edit mode
"</td>",
"</tr>"
].join("")
`<tr class="folder_row library-row" data-id="<%- content_item.id %>">
<td class="mid">
<span title="Folder" class="fa fa-folder-o"></span>
</td>
<td class="mid"><input style="margin: 0;" type="checkbox"></td>
<% if(!edit_mode) { %>
<td>
<a href="#folders/<%- content_item.id %>"><%- content_item.get("name") %></a>
</td>
<% if( content_item.get("description") ) { %>
<% if( content_item.get("description").length> 80 ) { %>
<td data-toggle="tooltip" data-placement="auto"
title='<%= _.escape(content_item.get("description")) %>'>
<%= _.escape(content_item.get("description")).substring(0, 80) + "..." %>
</td>
<% } else { %>
<td><%= _.escape(content_item.get("description"))%></td>
<% } %>
<% } else { %>
<td></td>
<% } %>
<% } else if(edit_mode){ %>
<td>
<textarea rows="4" class="form-control input_folder_name" placeholder="name" ><%- content_item.get("name") %></textarea>
</td>
<td>
<textarea rows="4" class="form-control input_folder_description" placeholder="description" ><%- content_item.get("description") %></textarea>
</td>
<% } %>
<td>folder</td>
<td></td>
<td>
<%= _.escape(content_item.get("update_time")) %>
</td>
<td></td>
<td class="right-center">
<% if(edit_mode) { %> <!-- start edit mode -->
<button data-toggle="tooltip" data-placement="top" title="Save changes"
class="primary-button btn-sm save_folder_btn" type="button"
style="<% if(button_config.save_folder_btn === false) { print("display:none;") } %>">
<span class="fa fa-floppy-o"></span> Save
</button>
<button data-toggle="tooltip" data-placement="top" title="Discard changes"
class="primary-button btn-sm cancel_folder_btn" type="button"
style="<% if(button_config.cancel_folder_btn === false) { print("display:none;") } %>">
<span class="fa fa-times"></span> Cancel
</button>
<% } else if (!edit_mode){%> <!-- start no edit mode -->
<button data-toggle="tooltip" data-placement="top"
title="Modify '<%- content_item.get("name") %>'"
class="primary-button btn-sm edit_folder_btn" type="button"
style="<% if(button_config.edit_folder_btn === false) { print("display:none;") } %>">
<span class="fa fa-pencil"></span> Edit
</button>
<a href="#/folders/<%- content_item.id %>/permissions">
<button data-toggle="tooltip" data-placement="top"
class="primary-button btn-sm permission_folder_btn"
title="Permissions of '<%- content_item.get("name") %>'"
style="<% if(button_config.permission_folder_btn === false) { print("display:none;") } %>">
<span class="fa fa-group"></span> Manage
</button>
</a>
<% } %> <!-- end no edit mode -->
</td>
</tr>`
);
},
templateRowFile: function() {
return _.template(
[
'<tr class="dataset_row light library-row" data-id="<%- content_item.id %>">',
'<td class="mid">',
'<span title="Dataset" class="fa fa-file-o"/>',
"</td>",
'<td class="mid">',
'<input style="margin: 0;" type="checkbox">',
"</td>",
"<td>",
'<a href="#folders/<%- content_item.get("folder_id") %>/datasets/<%- content_item.id %>" class="library-dataset">',
'<%- content_item.get("name") %>',
"<a>",
"</td>",
'<td><%- content_item.get("message") %></td>',
'<td><%= _.escape(content_item.get("file_ext")) %></td>',
'<td><%= _.escape(content_item.get("file_size")) %></td>',
'<td><%= _.escape(content_item.get("update_time")) %></td>',
"<td>",
'<% if ( content_item.get("state") !== "ok" ) { %>',
'<%= _.escape(content_item.get("state")) %>',
"<% } %>",
"</td>",
"<td>",
'<% if (content_item.get("is_unrestricted")) { %>',
'<span data-toggle="tooltip" data-placement="top" title="Unrestricted dataset" style="color:grey;" class="fa fa-globe"/>',
"<% } %>",
'<% if (content_item.get("is_private")) { %>',
'<span data-toggle="tooltip" data-placement="top" title="Private dataset" style="color:grey;" class="fa fa-key"/>',
"<% } %>",
'<% if ((content_item.get("is_unrestricted") === false) && (content_item.get("is_private") === false)) { %>',
'<span data-toggle="tooltip" data-placement="top" title="Restricted dataset" style="color:grey;" class="fa fa-shield"/>',
"<% } %>",
'<% if (content_item.get("can_manage")) { %>',
'<a href="#folders/<%- content_item.get("folder_id") %>/datasets/<%- content_item.id %>/permissions">',
'<button data-toggle="tooltip" data-placement="top" class="primary-button btn-sm permissions-dataset-btn" title="Permissions of \'<%- content_item.get("name") %>\'">',
'<span class="fa fa-group"/> Manage',
"</button>",
"</a>",
"<% } %>",
"</td>",
"</tr>"
].join("")
`<tr class="dataset_row library-row" data-id="<%- content_item.id %>">
<td class="mid">
<span title="Dataset" class="fa fa-file-o"></span>
</td>
<td class="mid">
<input style="margin: 0;" type="checkbox">
</td>
<td>
<a href="#folders/<%- content_item.get("folder_id") %>/datasets/<%- content_item.id %>"
class="library-dataset">
<%- content_item.get("name") %>
<a>
</td>
<td><%- content_item.get("message") %></td>
<td><%= _.escape(content_item.get("file_ext")) %></td>
<td><%= _.escape(content_item.get("file_size")) %></td>
<td><%= _.escape(content_item.get("update_time")) %></td>
<td>
<% if ( content_item.get("state") !== "ok" ) { %>
<%= _.escape(content_item.get("state")) %>
<% } %>
</td>
<td class="right-center">
<% if (content_item.get("is_unrestricted")) { %>
<span data-toggle="tooltip" data-placement="top" title="Unrestricted dataset"
class="fa fa-globe"></span>
<% } %>
<% if (content_item.get("is_private")) { %>
<span data-toggle="tooltip" data-placement="top" title="Private dataset"
class="fa fa-key"></span>
<% } %>
<% if ((content_item.get("is_unrestricted") === false) && (content_item.get("is_private") === false)) { %>
<span data-toggle="tooltip" data-placement="top" title="Restricted dataset"
class="fa fa-shield"></span>
<% } %>
<% if (content_item.get("can_manage")) { %>
<a href="#folders/<%- content_item.get("folder_id") %>/datasets/<%- content_item.id %>/permissions">
<button data-toggle="tooltip" data-placement="top"
class="primary-button btn-sm permissions-dataset-btn"
title="Permissions of '<%- content_item.get("name") %>'">
<span class="fa fa-group"></span> Manage
</button>
</a>
<% } %>
</td>
</tr>`
);
},
templateRowDeletedFile: function() {
return _.template(
[
'<tr class="active deleted_dataset library-row" data-id="<%- content_item.id %>">',
'<td class="mid">',
'<span title="Dataset" class="fa fa-file-o"/>',
"</td>",
"<td></td>",
'<td style="color:grey;">',
'<%- content_item.get("name") %>',
"</td>",
"<td>",
'<%- content_item.get("message") %>',
"</td>",
"<td>",
'<%= _.escape(content_item.get("file_ext")) %>',
"</td>",
"<td>",
'<%= _.escape(content_item.get("file_size")) %>',
"</td>",
"<td>",
'<%= _.escape(content_item.get("update_time")) %>',
"</td>",
"<td>",
'<% if ( content_item.get("state") !== "ok" ) { %>',
'<%= _.escape(content_item.get("state")) %>',
"<% } %>",
"</td>",
"<td>",
'<span data-toggle="tooltip" data-placement="top" title="Marked deleted" style="color:grey;" class="fa fa-ban"/>',
'<button data-toggle="tooltip" data-placement="top" title="Undelete \'<%- content_item.get("name") %>\'" class="primary-button btn-sm undelete_dataset_btn" type="button" style="margin-left:1em;">',
'<span class="fa fa-unlock"/> Undelete',
"</button>",
"</td>",
"</tr>"
].join("")
`<tr class="active deleted_dataset library-row" data-id="<%- content_item.id %>">
<td class="mid">
<span title="Dataset" class="fa fa-file-o"></span>
</td>
<td></td>
<td style="color:grey;">
<%- content_item.get("name") %>
</td>
<td>
<%- content_item.get("message") %>
</td>
<td>
<%= _.escape(content_item.get("file_ext")) %>
</td>
<td>
<%= _.escape(content_item.get("file_size")) %>
</td>
<td>
<%= _.escape(content_item.get("update_time")) %>
</td>
<td>
<% if ( content_item.get("state") !== "ok" ) { %>
<%= _.escape(content_item.get("state")) %>
<% } %>
</td>
<td class="right-center">
<span data-toggle="tooltip" data-placement="top"
title="Marked deleted" class="fa fa-ban"></span>
<button data-toggle="tooltip" data-placement="top"
title="Undelete '<%- content_item.get("name") %>'"
class="primary-button btn-sm undelete_dataset_btn" type="button" style="margin-left:1em;">
<span class="fa fa-unlock"></span> Undelete
</button>
</td>
</tr>`
);
},
templateRowDeletedFolder: function() {
return _.template(
[
'<tr class="active deleted_folder light library-row" data-id="<%- content_item.id %>">',
'<td class="mid">',
'<span title="Folder" class="fa fa-folder-o"/>',
"</td>",
"<td></td>",
'<td style="color:grey;">',
'<%- content_item.get("name") %>',
"</td>",
"<td>",
'<%- content_item.get("description") %>',
"</td>",
"<td>",
"folder",
"</td>",
"<td></td>",
"<td>",
'<%= _.escape(content_item.get("update_time")) %>',
"</td>",
"<td></td>",
"<td>",
'<span data-toggle="tooltip" data-placement="top" title="Marked deleted" style="color:grey;" class="fa fa-ban"/>',
'<button data-toggle="tooltip" data-placement="top" title="Undelete \'<%- content_item.get("name") %>\'" class="primary-button btn-sm undelete_folder_btn" type="button" style="margin-left:1em;">',
'<span class="fa fa-unlock"/> Undelete',
"</button>",
"</td>",
"</tr>"
].join("")
`<tr class="active deleted_folder library-row" data-id="<%- content_item.id %>">
<td class="mid">
<span title="Folder" class="fa fa-folder-o"></span>
</td>
<td></td>
<td style="color:grey;">
<%- content_item.get("name") %>
</td>
<% if( content_item.get("description") ) { %>
<% if( content_item.get("description").length> 80 ) { %>
<td data-toggle="tooltip" data-placement="bottom"
title='<%= _.escape(content_item.get("description")) %>'>
<%= _.escape(content_item.get("description")).substring(0, 80) + "..." %>
</td>
<% } else { %>
<td><%= _.escape(content_item.get("description"))%></td>
<% } %>
<% } else { %>
<td></td>
<% } %>
<td>
folder
</td>
<td></td>
<td>
<%= _.escape(content_item.get("update_time")) %>
</td>
<td></td>
<td class="right-center">
<span data-toggle="tooltip" data-placement="top"
title="Marked deleted" class="fa fa-ban"></span>
<button data-toggle="tooltip" data-placement="top"
title="Undelete '<%- content_item.get("name") %>'"
class="primary-button btn-sm undelete_folder_btn" type="button" style="margin-left:1em;">
<span class="fa fa-unlock"></span> Undelete
</button>
</td>
</tr>`
);
}
});
File diff suppressed because it is too large Load Diff
@@ -245,55 +245,58 @@ var LibraryView = Backbone.View.extend({
templateLibraryPermissions: function() {
return _.template(
[
'<div class="library_style_container">',
"<div>",
'<a href="#">',
'<button data-toggle="tooltip" data-placement="top" title="Go back to the list of Libraries" class="btn btn-secondary primary-button" type="button">',
'<span class="fa fa-list"/>',
"&nbsp;Libraries",
"</button>",
"</a>",
"</div>",
"<h1>",
'Library: <%= _.escape(library.get("name")) %>',
"</h1>",
'<div class="alert alert-warning">',
"<% if (is_admin) { %>",
"You are logged in as an <strong>administrator</strong> therefore you can manage any library on this Galaxy instance. Please make sure you understand the consequences.",
"<% } else { %>",
"You can assign any number of roles to any of the following permission types. However please read carefully the implications of such actions.",
"<% }%>",
"</div>",
'<div class="dataset_table">',
"<h2>Library permissions</h2>",
"<h4>Roles that can access the library</h4>",
'<div id="access_perm" class="access_perm roles-selection"/>',
'<div class="alert alert-info roles-selection">',
"User with <strong>any</strong> of these roles can access this library. If there are no access roles set on the library it is considered <strong>unrestricted</strong>.",
"</div>",
"<h4>Roles that can manage permissions on this library</h4>",
'<div id="manage_perm" class="manage_perm roles-selection"/>',
'<div class="alert alert-info roles-selection">',
"User with <strong>any</strong> of these roles can manage permissions on this library (includes giving access).",
"</div>",
"<h4>Roles that can add items to this library</h4>",
'<div id="add_perm" class="add_perm roles-selection"/>',
'<div class="alert alert-info roles-selection">',
"User with <strong>any</strong> of these roles can add items to this library (folders and datasets).",
"</div>",
"<h4>Roles that can modify this library</h4>",
'<div id="modify_perm" class="modify_perm roles-selection"/>',
'<div class="alert alert-info roles-selection">',
"User with <strong>any</strong> of these roles can modify this library (name, synopsis, etc.).",
"</div>",
'<button data-toggle="tooltip" data-placement="top" title="Save modifications made on this page" class="btn btn-secondary toolbtn_save_permissions primary-button" type="button">',
'<span class="fa fa-floppy-o"/>',
"&nbsp;Save",
"</button>",
"</div>",
"</div>"
].join("")
`<div class="library_style_container">
<div>
<a href="#">
<button data-toggle="tooltip" data-placement="top"
title="Go back to the list of Libraries" class="btn btn-secondary primary-button" type="button">
<span class="fa fa-list"></span>&nbsp;Libraries
</button>
</a>
</div>
<h1>
Library: <%= _.escape(library.get("name")) %>
</h1>
<div class="alert alert-warning">
<% if (is_admin) { %>
You are logged in as an <strong>administrator</strong> therefore you can manage any library
on this Galaxy instance. Please make sure you understand the consequences.
<% } else { %>
You can assign any number of roles to any of the following permission types.
However please read carefully the implications of such actions.
<% }%>
</div>
<div class="dataset_table">
<h2>Library permissions</h2>
<h4>Roles that can access the library</h4>
<div id="access_perm" class="access_perm roles-selection"></div>
<div class="alert alert-info roles-selection">
User with <strong>any</strong> of these roles can access this library.
If there are no access roles set on the library it is considered <strong>unrestricted</strong>.
</div>
<h4>Roles that can manage permissions on this library</h4>
<div id="manage_perm" class="manage_perm roles-selection"></div>
<div class="alert alert-info roles-selection">
User with <strong>any</strong> of these roles can manage permissions on this library
(includes giving access).
</div>
<h4>Roles that can add items to this library</h4>
<div id="add_perm" class="add_perm roles-selection"></div>
<div class="alert alert-info roles-selection">
User with <strong>any</strong> of these roles can add items to this library (folders and datasets).
</div>
<h4>Roles that can modify this library</h4>
<div id="modify_perm" class="modify_perm roles-selection"></div>
<div class="alert alert-info roles-selection">
User with <strong>any</strong> of these roles can modify this library (name, synopsis, etc.).
</div>
<button data-toggle="tooltip" data-placement="top"
title="Save modifications made on this page"
class="btn btn-secondary toolbtn_save_permissions primary-button" type="button">
<span class="fa fa-floppy-o"></span>&nbsp;Save
</button>
</div>
</div>`
);
}
});
@@ -224,45 +224,140 @@ var LibraryListView = Backbone.View.extend({
}
},
/**
* Create the new library inline
*/
createLibraryInline: function() {
if (this.$el.find("tr.new-row").length) {
this.$el.find("tr.new-row textarea")[0].focus();
} else {
const template = this.templateNewRow();
this.$el
.find("#library_list_body")
.closest("table")
.show();
this.$el.find("#library_list_body").prepend(template);
this.$el.find("tr.new-row textarea")[0].focus();
this.$el.find("tr.new-row .save_library_btn").click(() => {
this.createNewLibrary(
this.$el.find("tr.new-row textarea")[0].value,
this.$el.find("tr.new-row textarea")[1].value,
this.$el.find("tr.new-row textarea")[2].value
);
});
this.$el.find("tr.new-row .cancel_library_btn").click(() => {
this.$el.find("tr.new-row").remove();
});
}
},
/**
* Create the new library using the API asynchronously.
*/
createNewLibrary: function(name, description, synopsis) {
const Galaxy = getGalaxyInstance();
const libraryDetails = {
name,
description,
synopsis
};
if (libraryDetails.name !== "") {
var library = new mod_library_model.Library();
library.save(libraryDetails, {
success: library => {
Galaxy.libraries.libraryListView.collection.add(library);
this.$el.find("tr.new-row").remove();
Galaxy.libraries.libraryListView.render();
$(`tr[data-id="${library.attributes.id}"`)
.addClass("table-success")
.on("mouseover click", function() {
$(this).removeClass("table-success");
});
Toast.success("Library created.");
},
error: (model, response) => {
if (typeof response.responseJSON !== "undefined") {
Toast.error(response.responseJSON.err_msg);
} else {
Toast.error("An error occurred.");
}
}
});
} else {
Toast.error("Library's name is missing.");
}
return false;
},
// MMMMMMMMMMMMMMMMMM
// === TEMPLATES ====
// MMMMMMMMMMMMMMMMMM
templateNewRow: function() {
return _.template(
`<tr class="new-row">
<td>
<textarea name="input_library_name" rows="4" class="form-control input_library_name" placeholder="name" ></textarea>
</td>
<td>
<textarea rows="4" class="form-control input_library_description" placeholder="description"></textarea>
</td>
<td>
<textarea rows="4" class="form-control input_library_synopsis" placeholder="synopsis"></textarea>
</td>
<td class="right-center">
<button data-toggle="tooltip" data-placement="left" title="Save changes"
class="btn btn-secondary btn-sm save_library_btn" type="button">
<span class="fa fa-floppy-o"></span> Save
</button>
<button data-toggle="tooltip" data-placement="left" title="Discard changes"
class="btn btn-secondary btn-sm cancel_library_btn" type="button">
<span class="fa fa-times"></span> Cancel
</button>
</td>
</tr>`
);
},
templateLibraryList: function() {
return _.template(
[
'<div class="library_container table-responsive">',
"<% if(length === 0) { %>",
"<% if(search_term.length > 0) { %>",
"<div>",
"There are no libraries matching your search. Try different keyword.",
"</div>",
"<% } else{ %>",
"<div>",
"There are no libraries visible to you here. If you expected some to show up please consult the",
' <a href="https://galaxyproject.org/data-libraries/#permissions" target="_blank">library security wikipage</a>.',
"</div>",
"<% }%>",
"<% } else{ %>",
'<table class="grid table table-sm">',
"<thead>",
'<th style="width:30%;">',
'<a class="sort-libraries-link" title="Click to reverse order" href="#">',
"name",
"</a>",
'<span title="Sorted alphabetically" class="fa fa-sort-alpha-<%- order %>"/>',
"</th>",
'<th style="width:22%;">description</th>',
'<th style="width:22%;">synopsis</th> ',
'<th style="width:26%;"></th>',
"</thead>",
'<tbody id="library_list_body">',
// library item views will attach here
"</tbody>",
"</table>",
"<% }%>",
"</div>"
].join("")
`<div class="library_container table-responsive">
<table class="grid table table-sm"
<% if(length === 0) { %> style="display:none;" <% }%> >
<thead>
<th style="width:30%;">
<a class="sort-libraries-link" title="Click to reverse order" href="#">
Name
</a>
<span title="Sorted alphabetically" class="fa fa-sort-alpha-<%- order %>"></span>
</th>
<th style="width:22%;">Description</th>
<th style="width:22%;">Synopsis</th>
<th style="width:26%;"></th>
</thead>
<tbody id="library_list_body">
<!-- library item views will attach here -->
</tbody>
</table>
<% if(length === 0) { %>
<% if(search_term.length > 0) { %>
<div>
There are no libraries matching your search. Try different keyword.
</div>
<% } else{ %>
<div>
There are no libraries visible to you here. If you expected some to show up please consult the
<a href="https://galaxyproject.org/data-libraries/#permissions" target="_blank">library security wikipage</a>.
</div>
<% }%>
<% }%>
</div>`
);
}
});
@@ -232,52 +232,96 @@ var LibraryRowView = Backbone.View.extend({
templateRow: function() {
return _.template(
[
'<tr class="<% if(library.get("deleted") === true) { print("active") } %>" style="display:none;" data-id="<%- library.get("id") %>">',
"<% if(!edit_mode) { %>",
'<% if(library.get("deleted")) { %>',
'<td style="color:grey;"><%- library.get("name") %></td>',
"<% } else { %>",
'<td><a href="#folders/<%- library.get("root_folder_id") %>"><%- library.get("name") %></a></td>',
"<% } %>",
'<% if(library.get("description")) { %>',
'<% if( (library.get("description")).length> 80 ) { %>',
'<td data-toggle="tooltip" data-placement="bottom" title="<%= _.escape(library.get("description")) %>"><%= _.escape(library.get("description")).substring(0, 80) + "..." %></td>',
"<% } else { %>",
'<td><%= _.escape(library.get("description"))%></td>',
"<% } %>",
"<% } else { %>",
"<td></td>",
"<% } %>",
'<% if(library.get("synopsis")) { %>',
'<% if( (library.get("synopsis")).length> 120 ) { %>',
'<td data-toggle="tooltip" data-placement="bottom" title="<%= _.escape(library.get("synopsis")) %>"><%= _.escape(library.get("synopsis")).substring(0, 120) + "..." %></td>',
"<% } else { %>",
'<td><%= _.escape(library.get("synopsis"))%></td>',
"<% } %>",
"<% } else { %>",
"<td></td>",
"<% } %>",
"<% } else if(edit_mode){ %>",
'<td><textarea rows="4" class="form-control input_library_name" placeholder="name" ><%- library.get("name") %></textarea></td>',
'<td><textarea rows="4" class="form-control input_library_description" placeholder="description" ><%- library.get("description") %></textarea></td>',
'<td><textarea rows="4" class="form-control input_library_synopsis" placeholder="synopsis" ><%- library.get("synopsis") %></textarea></td>',
"<% } %>",
'<td class="right-center">',
'<% if( (library.get("public")) && (library.get("deleted") === false) ) { %>',
'<span data-toggle="tooltip" data-placement="left" title="Unrestricted library" style="color:grey;" class="fa fa-globe fa-lg"> </span>',
"<% }%>",
'<button data-toggle="tooltip" data-placement="left" title="Modify \'<%- library.get("name") %>\'" class="btn btn-sm edit_library_btn" type="button" style="<% if(button_config.edit_library_btn === false) { print("display:none;") } %>"><span class="fa fa-pencil"></span> Edit</button>',
'<a data-toggle="tooltip" data-placement="left" title="Permissions of \'<%- library.get("name") %>\'" href="#library/<%- library.get("id") %>/permissions"><button class="btn btn-sm permission_library_btn" type="button" style="<% if(button_config.permission_library_btn === false) { print("display:none;") } %>"><span class="fa fa-group"></span> Manage</button></a>',
'<button data-toggle="tooltip" data-placement="left" title="Save changes" class="btn btn-sm save_library_btn" type="button" style="<% if(button_config.save_library_btn === false) { print("display:none;") } %>"><span class="fa fa-floppy-o"></span> Save</button>',
'<button data-toggle="tooltip" data-placement="left" title="Discard changes" class="btn btn-sm cancel_library_btn" type="button" style="<% if(button_config.cancel_library_btn === false) { print("display:none;") } %>"><span class="fa fa-times"></span> Cancel</button>',
'<button data-toggle="tooltip" data-placement="left" title="Delete \'<%- library.get("name") %>\'" class="btn btn-sm delete_library_btn" type="button" style="<% if(button_config.delete_library_btn === false) { print("display:none;") } %>"><span class="fa fa-trash-o"></span> Delete</button>',
// For deleted libraries
'<span data-toggle="tooltip" data-placement="left" title="Marked deleted" style="color:grey; <% if(button_config.undelete_library_btn === false) { print("display:none;") } %>" class="fa fa-ban fa-lg"></span>',
'<button data-toggle="tooltip" data-placement="left" title="Undelete \'<%- library.get("name") %>\' " class="btn btn-sm undelete_library_btn" type="button" style="<% if(button_config.undelete_library_btn === false) { print("display:none;") } %>"><span class="fa fa-unlock"></span> Undelete</button>',
"</td>",
"</tr>"
].join("")
`<tr class="<% if(library.get("deleted") === true) { print("active") } %>"
style="display:none;" data-id="<%- library.get("id") %>">
<% if(!edit_mode) { %>
<% if(library.get("deleted")) { %>
<td style="color:grey;"><%- library.get("name") %></td>
<% } else { %>
<td>
<a href="#folders/<%- library.get("root_folder_id") %>"><%- library.get("name") %></a>
</td>
<% } %>
<% if(library.get("description")) { %>
<% if( (library.get("description")).length> 80 ) { %>
<td data-toggle="tooltip" data-placement="bottom"
title="<%= _.escape(library.get("description")) %>">
<%= _.escape(library.get("description")).substring(0, 80) + "..." %>
</td>
<% } else { %>
<td><%= _.escape(library.get("description"))%></td>
<% } %>
<% } else { %>
<td></td>
<% } %>
<% if(library.get("synopsis")) { %>
<% if( (library.get("synopsis")).length> 120 ) { %>
<td data-toggle="tooltip" data-placement="bottom"
title="<%= _.escape(library.get("synopsis")) %>">
<%= _.escape(library.get("synopsis")).substring(0, 120) + "..." %>
</td>
<% } else { %>
<td><%= _.escape(library.get("synopsis"))%></td>
<% } %>
<% } else { %>
<td></td>
<% } %>
<% } else if(edit_mode){ %>
<td>
<textarea rows="4" class="form-control input_library_name" placeholder="name" ><%- library.get("name") %></textarea>
</td>
<td>
<textarea rows="4" class="form-control input_library_description" placeholder="description"><%- library.get("description") %></textarea>
</td>
<td>
<textarea rows="4" class="form-control input_library_synopsis" placeholder="synopsis"><%- library.get("synopsis") %></textarea>
</td>
<% } %>
<td class="right-center">
<% if( (library.get("public")) && (library.get("deleted") === false) ) { %>
<span data-toggle="tooltip" data-placement="left" title="Unrestricted library"
class="fa fa-globe fa-lg"></span>
<% }%>
<button data-toggle="tooltip" data-placement="left" title="Modify '<%- library.get("name") %>'"
class="btn btn-secondary btn-sm edit_library_btn" type="button"
style="<% if(button_config.edit_library_btn === false) { print("display:none;") } %>">
<span class="fa fa-pencil"></span> Edit
</button>
<a data-toggle="tooltip" data-placement="left"
title="Permissions of '<%- library.get("name") %>'"
href="#library/<%- library.get("id") %>/permissions">
<button class="btn btn-secondary btn-sm permission_library_btn" type="button"
style="<% if(button_config.permission_library_btn === false) { print("display:none;") } %>">
<span class="fa fa-group"></span> Manage
</button>
</a>
<button data-toggle="tooltip" data-placement="left" title="Save changes"
class="btn btn-secondary btn-sm save_library_btn" type="button"
style="<% if(button_config.save_library_btn === false) { print("display:none;") } %>">
<span class="fa fa-floppy-o"></span> Save
</button>
<button data-toggle="tooltip" data-placement="left" title="Discard changes"
class="btn btn-secondary btn-sm cancel_library_btn" type="button"
style="<% if(button_config.cancel_library_btn === false) { print("display:none;") } %>">
<span class="fa fa-times"></span> Cancel
</button>
<button data-toggle="tooltip" data-placement="left" title="Delete '<%- library.get("name") %>'"
class="btn btn-secondary btn-sm delete_library_btn" type="button"
style="<% if(button_config.delete_library_btn === false) { print("display:none;") } %>">
<span class="fa fa-trash-o"></span> Delete
</button>
<!-- For deleted libraries -->
<span data-toggle="tooltip" data-placement="left" title="Marked deleted"
style="color:grey; <% if(button_config.undelete_library_btn === false) { print("display:none;") } %>"
class="fa fa-ban fa-lg">
</span>
<button data-toggle="tooltip" data-placement="left" title="Undelete '<%- library.get("name") %>' "
class="btn btn-secondary btn-sm undelete_library_btn" type="button"
style="<% if(button_config.undelete_library_btn === false) { print("display:none;") } %>">
<span class="fa fa-unlock"></span> Undelete
</button>
</td>
</tr>`
);
}
});
@@ -1,9 +1,6 @@
import _ from "underscore";
import $ from "jquery";
import Backbone from "backbone";
import _l from "utils/localization";
import { Toast } from "ui/toast";
import mod_library_model from "mvc/library/library-model";
import { getGalaxyInstance } from "app";
/**
@@ -18,10 +15,11 @@ var LibraryToolbarView = Backbone.View.extend({
},
events: {
"click #create_new_library_btn": "createLibraryFromModal",
"click #create_new_library_btn": "createLibraryInline",
"click #include_deleted_chk": "includeDeletedChecked",
"click #exclude_restricted_chk": "excludeRestrictedChecked",
"click .page_size_prompt": "showPageSizePrompt",
"keydown .page_size": "changePageSize",
"blur .page_size": "changePageSize",
"keyup .library-search-input": "searchLibraries"
},
@@ -39,7 +37,13 @@ var LibraryToolbarView = Backbone.View.extend({
is_admin = Galaxy.user.isAdmin();
is_anonym = Galaxy.user.isAnonymous();
}
this.$el.html(toolbar_template({ admin_user: is_admin, anon_user: is_anonym }));
this.$el.html(
toolbar_template({
admin_user: is_admin,
anon_user: is_anonym,
library_page_size: Galaxy.libraries.preferences.get("library_page_size")
})
);
if (is_admin) {
this.$el.find("#include_deleted_chk")[0].checked = Galaxy.libraries.preferences.get("with_deleted");
this.$el.find("#exclude_restricted_chk")[0].checked = Galaxy.libraries.preferences.get(
@@ -55,7 +59,8 @@ var LibraryToolbarView = Backbone.View.extend({
const Galaxy = getGalaxyInstance();
this.options = _.extend(this.options, options);
var paginator_template = this.templatePaginator();
this.$el.find(".library-paginator").html(
var paginator_text_template = this.templatePaginatorText();
this.$el.find(".library-paginator > .paginator").html(
paginator_template({
show_page: parseInt(this.options.show_page),
page_count: parseInt(this.options.page_count),
@@ -64,109 +69,39 @@ var LibraryToolbarView = Backbone.View.extend({
library_page_size: Galaxy.libraries.preferences.get("library_page_size")
})
);
this.$el.find(".library-paginator > .paginator-text").html(
paginator_text_template({
show_page: parseInt(this.options.show_page),
page_count: parseInt(this.options.page_count),
total_libraries_count: this.options.total_libraries_count,
libraries_shown: this.options.libraries_shown,
library_page_size: Galaxy.libraries.preferences.get("library_page_size")
})
);
},
/**
* User clicked on 'New library' button. Show modal to
* satisfy the wish.
* Crreate the new library inline
*/
createLibraryFromModal: function(event) {
event.preventDefault();
event.stopPropagation();
createLibraryInline: function() {
const Galaxy = getGalaxyInstance();
var self = this;
this.modal = Galaxy.modal;
this.modal.show({
closing_events: true,
title: _l("Create New Library"),
body: this.templateNewLibraryInModal(),
buttons: {
Create: function() {
self.createNewLibrary();
},
Close: function() {
self.modal.hide();
}
}
});
Galaxy.libraries.libraryListView.createLibraryInline();
},
/**
* Create the new library using the API asynchronously.
* Change the number of libs shown on page.
*/
createNewLibrary: function() {
const Galaxy = getGalaxyInstance();
var libraryDetails = this.serializeNewLibrary();
if (this.validateNewLibrary(libraryDetails)) {
var library = new mod_library_model.Library();
var self = this;
library.save(libraryDetails, {
success: function(library) {
Galaxy.libraries.libraryListView.collection.add(library);
self.modal.hide();
self.clearLibraryModal();
Galaxy.libraries.libraryListView.render();
Toast.success("Library created.");
},
error: function(model, response) {
if (typeof response.responseJSON !== "undefined") {
Toast.error(response.responseJSON.err_msg);
} else {
Toast.error("An error occurred.");
}
}
});
} else {
Toast.error("Library's name is missing.");
}
return false;
},
/**
* Show user the propmpt to change the number of libs shown on page.
*/
showPageSizePrompt: function(e) {
e.preventDefault();
const Galaxy = getGalaxyInstance();
var library_page_size = prompt(
"How many libraries per page do you want to see?",
Galaxy.libraries.preferences.get("library_page_size")
);
if (library_page_size != null && library_page_size == parseInt(library_page_size)) {
changePageSize: function(e) {
if (e.type === "focusout" || (e.type === "keydown" && e.keyCode === 13)) {
e.preventDefault();
const Galaxy = getGalaxyInstance();
Galaxy.libraries.preferences.set({
library_page_size: parseInt(library_page_size)
library_page_size: parseInt(e.target.value)
});
Galaxy.libraries.libraryListView.render({ show_page: 1 });
}
},
/**
* Clear the library modal once it is saved.
*/
clearLibraryModal: function() {
$("input[name='Name']").val("");
$("input[name='Description']").val("");
$("input[name='Synopsis']").val("");
},
/**
* Prepare new library variables to be submitted to API.
*/
serializeNewLibrary: function() {
return {
name: $("input[name='Name']").val(),
description: $("input[name='Description']").val(),
synopsis: $("input[name='Synopsis']").val()
};
},
/**
* Check whether entered values are valid.
*/
validateNewLibrary: function(libraryDetails) {
return libraryDetails.name !== "";
},
/**
* Include or exclude deleted libraries in the view.
*/
@@ -212,17 +147,14 @@ var LibraryToolbarView = Backbone.View.extend({
<a class="btn btn-secondary mr-1" data-toggle="tooltip" data-placement="top" title="Go to first page" href="#">
<span class="fa fa-home"/>
</a>
<a class="library-help-button btn btn-secondary mr-1" data-toggle="tooltip" title="See this screen annotated" href="https://galaxyproject.org/data-libraries/screen/list-of-libraries/" target="_blank">
<span class="fa fa-question"/>
</a>
<% if(admin_user === true) { %>
<button data-toggle="tooltip" data-placement="top" title="Create new library" id="create_new_library_btn" class="mr-1 btn btn-secondary" type="button">
<span class="fa fa-plus"/>
<span class="fa fa-plus" /> Library
</button>
<% } %>
<div class="d-flex align-items-center library-paginator mr-1" />
<div class="d-flex align-items-center mr-1" />
<form class="form-inline mr-1">
<input type="text" class="form-control library-search-input mr-1" placeholder="Great Library" size="15">
<input type="text" id="library-filter" class="form-control library-search-input mr-1" placeholder="Search" size="15">
<% if(admin_user === true) { %>
<div class="form-check mr-1">
<input class="form-check-input" id="include_deleted_chk" type="checkbox"/>
@@ -236,6 +168,12 @@ var LibraryToolbarView = Backbone.View.extend({
</form>
</div>
<div id="libraries_element" />
<div class="d-flex justify-content-center align-items-center library-paginator mt-2 mb-2">
<ul class="pagination paginator mr-1" />
<input style="width: initial;" min="0" max="999" class="page_size form-control" type="number" value="<%- library_page_size %>" />
<span class="text-muted ml-1 paginator-text" />
</div>
</div>
</div>`
);
@@ -243,53 +181,76 @@ var LibraryToolbarView = Backbone.View.extend({
templatePaginator: function() {
return _.template(
[
'<ul class="pagination mr-1">',
"<% if ( ( show_page - 1 ) > 0 ) { %>",
"<% if ( ( show_page - 1 ) > page_count ) { %>", // we are on higher page than total page count
'<li class="page-item"><a class="page-link" href="#page/1"><span class="fa fa-angle-double-left"></span></a></li>',
'<li class="page-item disabled"><a class="page-link" href="#page/<% print( show_page ) %>"><% print( show_page - 1 ) %></a></li>',
"<% } else { %>",
'<li class="page-item"><a class="page-link" href="#page/1"><span class="fa fa-angle-double-left"></span></a></li>',
'<li class="page-item"><a class="page-link" href="#page/<% print( show_page - 1 ) %>"><% print( show_page - 1 ) %></a></li>',
"<% } %>",
"<% } else { %>", // we are on the first page
'<li class="page-item disabled"><a class="page-link" href="#page/1"><span class="fa fa-angle-double-left"></span></a></li>',
'<li class="page-item disabled"><a class="page-link" href="#page/<% print( show_page ) %>"><% print( show_page - 1 ) %></a></li>',
"<% } %>",
'<li class="page-item active">',
'<a class="page-link" href="#page/<% print( show_page ) %>"><% print( show_page ) %></a>',
"</li>",
"<% if ( ( show_page ) < page_count ) { %>",
'<li class="page-item"><a class="page-link" href="#page/<% print( show_page + 1 ) %>"><% print( show_page + 1 ) %></a></li>',
'<li class="page-item"><a class="page-link" href="#page/<% print( page_count ) %>"><span class="fa fa-angle-double-right"></span></a></li>',
"<% } else { %>",
'<li class="page-item disabled"><a class="page-link" href="#page/<% print( show_page ) %>"><% print( show_page + 1 ) %></a></li>',
'<li class="page-item disabled"><a class="page-link" href="#page/<% print( page_count ) %>"><span class="fa fa-angle-double-right"></span></a></li>',
"<% } %>",
"</ul>",
'<span class="mr-1">',
' <%- libraries_shown %> libraries shown <a href="" data-toggle="tooltip" data-placement="top" title="currently <%- library_page_size %> per page" class="page_size_prompt">(change)</a>',
"</span>",
'<span class="mr-1">',
" <%- total_libraries_count %> total",
"</span>"
].join("")
`
<% if ( ( show_page - 1 ) > 0 ) { %>
<% if ( ( show_page - 1 ) > page_count ) { %> <!-- we are on higher page than total page count -->
<li class="page-item">
<a class="page-link" href="#page/1">
<span class="fa fa-angle-double-left"></span>
</a>
</li>
<li class="page-item disabled">
<a class="page-link" href="#page/<% print( show_page ) %>">
<% print( show_page - 1 ) %>
</a>
</li>
<% } else { %>
<li class="page-item">
<a class="page-link" href="#page/1">
<span class="fa fa-angle-double-left"></span>
</a>
</li>
<li class="page-item">
<a class="page-link" href="#page/<% print( show_page - 1 ) %>">
<% print( show_page - 1 ) %>
</a>
</li>
<% } %>
<% } else { %> <!-- we are on the first page -->
<li class="page-item disabled">
<a class="page-link" href="#page/1">
<span class="fa fa-angle-double-left"></span>
</a>
</li>
<li class="page-item disabled">
<a class="page-link" href="#page/<% print( show_page ) %>">
<% print( show_page - 1 ) %>
</a>
</li>
<% } %>
<li class="page-item active">
<a class="page-link" href="#page/<% print( show_page ) %>">
<% print( show_page ) %>
</a>
</li>
<% if ( ( show_page ) < page_count ) { %>
<li class="page-item">
<a class="page-link" href="#page/<% print( show_page + 1 ) %>">
<% print( show_page + 1 ) %>
</a>
</li>
<li class="page-item">
<a class="page-link" href="#page/<% print( page_count ) %>">
<span class="fa fa-angle-double-right"></span>
</a>
</li>
<% } else { %>
<li class="page-item disabled">
<a class="page-link" href="#page/<% print( show_page ) %>">
<% print( show_page + 1 ) %>
</a>
</li>
<li class="page-item disabled">
<a class="page-link" href="#page/<% print( page_count ) %>">
<span class="fa fa-angle-double-right"></span>
</a>
</li>
<% } %>
`
);
},
templateNewLibraryInModal: function() {
return _.template(
[
'<div id="new_library_modal">',
"<form>",
'<input type="text" name="Name" value="" placeholder="Name" autofocus>',
'<input type="text" name="Description" value="" placeholder="Description">',
'<input type="text" name="Synopsis" value="" placeholder="Synopsis">',
"</form>",
"</div>"
].join("")
);
templatePaginatorText: function() {
return _.template(`per page, <%- total_libraries_count %> total`);
}
});
@@ -75,15 +75,9 @@ export default FormBase.extend({
`<b>${options.name}</b> ${options.description} (Galaxy Version ${options.version})`,
operations: !options.hide_operations && this._operations(),
onchange: function() {
const Galaxy = getGalaxyInstance();
self.deferred.reset();
self.deferred.execute(process => {
self.model.get("postchange")(process, self);
if (self.model.get("listen_to_history")) {
process.then(() => {
self.stopListening(Galaxy.currHistoryPanel.collection);
});
}
});
}
});
+4 -1
View File
@@ -242,7 +242,10 @@ var View = Backbone.View.extend({
toolId: job_def.tool_id
});
}
Galaxy.currHistoryPanel && Galaxy.currHistoryPanel.refreshContents();
if (Galaxy.currHistoryPanel) {
self.form.stopListening(Galaxy.currHistoryPanel.collection);
Galaxy.currHistoryPanel.refreshContents();
}
},
error: function(response) {
callback && callback();
+22 -150
View File
@@ -5,7 +5,7 @@
import $ from "jquery";
import _ from "underscore";
import Backbone from "backbone";
var PopupMenu = Backbone.View.extend({
const PopupMenu = Backbone.View.extend({
//TODO: maybe better as singleton off the Galaxy obj
/** Cache the desired button element and options, set up the button click handler
* NOTE: attaches this view as HTML/jQ data on the button for later use.
@@ -20,11 +20,10 @@ var PopupMenu = Backbone.View.extend({
this.$button.data("popupmenu", this);
// set up button click -> open menu behavior
var menu = this;
this.$button.click(event => {
// if there's already a menu open, remove it
$(".popmenu-wrapper").remove();
menu._renderAndShow(event);
this._renderAndShow(event);
return false;
});
},
@@ -51,13 +50,12 @@ var PopupMenu = Backbone.View.extend({
// set up behavior on each link/anchor elem
if (this.options.length) {
var menu = this;
this.$(".popupmenu-option").each(function(i) {
var option = menu.options[i];
this.$(".popupmenu-option").each((i, el) => {
const option = this.options[i];
// if the option has 'func', call that function when the anchor is clicked
if (option.func) {
$(this).click(event => {
option.func.call(menu, event, option);
$(el).click(event => {
option.func.call(this, event, option);
// We must preventDefault otherwise clicking "cancel"
// on a purge or something still navigates and causes
// the action.
@@ -86,9 +84,9 @@ var PopupMenu = Backbone.View.extend({
} else if (option.header) {
return `<div class="popupmenu-option dropdown-header">${option.html}</div>`;
}
var href = option.href || "javascript:void(0);";
var target = option.target ? `target="${option.target}"` : "";
var check = option.checked ? '<span class="fa fa-check mr-1"/>' : "";
const href = option.href || "javascript:void(0);";
const target = option.target ? `target="${option.target}"` : "";
const check = option.checked ? '<span class="fa fa-check mr-1"/>' : "";
return `<a class="popupmenu-option dropdown-item" href="${href}" ${target}>${check}${option.html}</a>`;
}).join("");
},
@@ -96,8 +94,8 @@ var PopupMenu = Backbone.View.extend({
// get the absolute position/offset for the menu
_getShownPosition: function(clickEvent) {
// display menu horiz. centered on click...
var menuWidth = this.$el.width();
var x = clickEvent.pageX - menuWidth / 2;
const menuWidth = this.$el.width();
let x = clickEvent.pageX - menuWidth / 2;
// adjust to handle horiz. scroll and window dimensions ( draw entirely on visible screen area )
x = Math.min(x, $(document).scrollLeft() + $(window).width() - menuWidth - 5);
@@ -111,11 +109,14 @@ var PopupMenu = Backbone.View.extend({
// bind an event handler to all available frames so that when anything is clicked
// the menu is removed from the DOM and the event handler unbinds itself
_setUpCloseBehavior: function() {
var menu = this;
//TODO: alternately: focus hack, blocking overlay, jquery.blockui
// function to close popup and unbind itself
function closePopup(event) {
const closePopup = event => {
//do nothing if header item is clicked
if (event.target.classList.contains("dropdown-header")) {
return;
}
$(document).off("click.close_popup");
try {
if (window && window.parent !== window) {
@@ -134,19 +135,19 @@ var PopupMenu = Backbone.View.extend({
console.debug(err);
}
}
menu.remove();
}
this.remove();
};
$("html").one("click.close_popup", closePopup);
$("html").on("click.close_popup", closePopup);
try {
if (window && window.parent !== window) {
$(window.parent.document)
.find("html")
.one("click.close_popup", closePopup);
.on("click.close_popup", closePopup);
} else {
$("iframe#galaxy_main")
.contents()
.one("click.close_popup", closePopup);
.on("click.close_popup", closePopup);
}
} catch (err) {
if (err instanceof DOMException) {
@@ -177,7 +178,7 @@ var PopupMenu = Backbone.View.extend({
// search for a menu option by its html
findIndexByHtml: function(html) {
for (var i = 0; i < this.options.length; i++) {
for (let i = 0; i < this.options.length; i++) {
if (_.has(this.options[i], "html") && this.options[i].html === html) {
return i;
}
@@ -200,134 +201,5 @@ PopupMenu.create = function _create($button, options) {
return new PopupMenu($button, options);
};
// -----------------------------------------------------------------------------
// the following class functions are bridges from the original make_popupmenu and make_popup_menus
// to the newer backbone.js PopupMenu
/** Create a PopupMenu from simple map initial_options activated by clicking button_element.
* Converts initial_options to object array used by PopupMenu.
* @param {jQuery|DOMElement} button_element element which, when clicked, activates menu
* @param {Object} initial_options map of key -> values, where
* key is option text, value is fn to call when option is clicked
* @returns {PopupMenu} the PopupMenu created
*/
PopupMenu.make_popupmenu = (button_element, initial_options) => {
var convertedOptions = [];
_.each(initial_options, (optionVal, optionKey) => {
var newOption = { html: optionKey };
// keys with null values indicate: header
if (optionVal === null) {
// !optionVal? (null only?)
newOption.header = true;
// keys with function values indicate: a menu option
} else if ($.type(optionVal) === "function") {
newOption.func = optionVal;
}
//TODO:?? any other special optionVals?
// there was no divider option originally
convertedOptions.push(newOption);
});
return new PopupMenu($(button_element), convertedOptions);
};
/** Find all anchors in $parent (using selector) and covert anchors into a PopupMenu options map.
* @param {jQuery} $parent the element that contains the links to convert to options
* @param {String} selector jq selector string to find links
* @returns {Object[]} the options array to initialize a PopupMenu
*/
//TODO: lose parent and selector, pass in array of links, use map to return options
PopupMenu.convertLinksToOptions = ($parent, selector) => {
$parent = $($parent);
selector = selector || "a";
var options = [];
$parent.find(selector).each((elem, i) => {
var option = {};
var $link = $(elem);
// convert link text to the option text (html) and the href into the option func
option.html = $link.text();
if ($link.attr("href")) {
var linkHref = $link.attr("href");
var linkTarget = $link.attr("target");
var confirmText = $link.attr("confirm");
option.func = () => {
// if there's a "confirm" attribute, throw up a confirmation dialog, and
// if the user cancels - do nothing
if (confirmText && !confirm(confirmText)) {
return;
}
// if there's no confirm attribute, or the user accepted the confirm dialog:
switch (linkTarget) {
// relocate the center panel
case "_parent":
window.parent.location = linkHref;
break;
// relocate the entire window
case "_top":
window.top.location = linkHref;
break;
// relocate this panel
default:
window.location = linkHref;
}
};
}
options.push(option);
});
return options;
};
/** Create a single popupmenu from existing DOM button and anchor elements
* @param {jQuery} $buttonElement the element that when clicked will open the menu
* @param {jQuery} $menuElement the element that contains the anchors to convert into a menu
* @param {String} menuElementLinkSelector jq selector string used to find anchors to be made into menu options
* @returns {PopupMenu} the PopupMenu (Backbone View) that can render, control the menu
*/
PopupMenu.fromExistingDom = ($buttonElement, $menuElement, menuElementLinkSelector) => {
$buttonElement = $($buttonElement);
$menuElement = $($menuElement);
var options = PopupMenu.convertLinksToOptions($menuElement, menuElementLinkSelector);
// we're done with the menu (having converted it to an options map)
$menuElement.remove();
return new PopupMenu($buttonElement, options);
};
/** Create all popupmenus within a document or a more specific element
* @param {DOMElement} parent the DOM element in which to search for popupmenus to build (defaults to document)
* @param {String} menuSelector jq selector string to find popupmenu menu elements (defaults to "div[popupmenu]")
* @param {Function} buttonSelectorBuildFn the function to build the jq button selector.
* Will be passed $menuElement, parent.
* (Defaults to return '#' + $menuElement.attr( 'popupmenu' ); )
* @returns {PopupMenu[]} array of popupmenus created
*/
PopupMenu.make_popup_menus = (parent, menuSelector, buttonSelectorBuildFn) => {
parent = parent || document;
// orig. Glx popupmenu menus have a (non-std) attribute 'popupmenu'
// which contains the id of the button that activates the menu
menuSelector = menuSelector || "div[popupmenu]";
// default to (orig. Glx) matching button to menu by using the popupmenu attr of the menu as the id of the button
buttonSelectorBuildFn = buttonSelectorBuildFn || (($menuElement, parent) => `#${$menuElement.attr("popupmenu")}`);
// aggregate and return all PopupMenus
var popupMenusCreated = [];
$(parent)
.find(menuSelector)
.each(function() {
var $menuElement = $(this);
var $buttonElement = $(parent).find(buttonSelectorBuildFn($menuElement, parent));
popupMenusCreated.push(PopupMenu.fromDom($buttonElement, $menuElement));
$buttonElement.addClass("popup");
});
return popupMenusCreated;
};
// =============================================================================
export default PopupMenu;
@@ -356,7 +356,7 @@ const View = Backbone.View.extend({
},
{
multiple: cnf.multiple,
library: cnf.library,
library: !!cnf.library,
format: null
}
);
+15 -43
View File
@@ -1,11 +1,9 @@
import $ from "jquery";
import Backbone from "backbone";
import Utils from "utils/utils";
var View = Backbone.View.extend({
const View = Backbone.View.extend({
initialize: function(options) {
var self = this;
this.model =
(options && options.model) ||
new Backbone.Model({
@@ -26,51 +24,25 @@ var View = Backbone.View.extend({
this.$slider = this.$(".ui-form-slider-element");
// add text field event
var pressed = [];
this.$text
.on("change", function() {
self.value($(this).val());
.on("change", e => {
this.value(e.currentTarget.value);
})
.on("keyup", e => {
pressed[e.which] = false;
})
.on("keydown", function(e) {
var v = e.which;
pressed[v] = true;
if (self.model.get("is_workflow") && pressed[16] && v == 52) {
self.value("$");
event.preventDefault();
} else if (
!(
v == 8 ||
v == 9 ||
v == 13 ||
v == 37 ||
v == 39 ||
(v >= 48 && v <= 57) ||
(v >= 96 && v <= 105) ||
((v == 190 || v == 110) &&
$(this)
.val()
.indexOf(".") == -1 &&
self.model.get("precise")) ||
((v == 189 || v == 109) &&
$(this)
.val()
.indexOf("-") == -1) ||
self._isParameter($(this).val()) ||
pressed[91] ||
pressed[17]
)
) {
event.preventDefault();
.on("input", e => {
const input = e.currentTarget;
if (this._isParameter(input.value)) {
return;
}
else if (!this.model.get("precise")) {
input.value = input.value.split(".")[0];
}
input.value = input.value.replace(/[^0-9eE.-]/g, '');
});
// build slider, cannot be rebuild in render
var opts = this.model.attributes;
const opts = this.model.attributes;
this.has_slider = opts.max !== null && opts.min !== null && opts.max > opts.min;
var step = opts.step;
let step = opts.step;
if (!step) {
if (opts.precise && this.has_slider) {
step = (opts.max - opts.min) / opts.split;
@@ -80,7 +52,7 @@ var View = Backbone.View.extend({
}
if (this.has_slider) {
this.$slider.slider({ min: opts.min, max: opts.max, step: step }).on("slide", (event, ui) => {
self.value(ui.value);
this.value(ui.value);
});
}
@@ -90,7 +62,7 @@ var View = Backbone.View.extend({
},
render: function() {
var value = this.model.get("value");
const value = this.model.get("value");
if (this.has_slider) {
this.$slider.slider("value", value);
this.$slider.show();
@@ -2,7 +2,6 @@
import _ from "underscore";
import $ from "jquery";
import Backbone from "backbone";
import { getAppRoot } from "onload/loadConfig";
import { getGalaxyInstance } from "app";
import Utils from "utils/utils";
import UploadUtils from "mvc/upload/upload-utils";
@@ -15,7 +14,7 @@ export default Backbone.View.extend({
class_remove: "upload-icon-button fa fa-check-square-o",
class_partial: "upload-icon-button fa fa-minus-square-o",
help_enabled: true,
oidc_text: `<br/>If you are signed-in to Galaxy using a third-party identity and you <strong>don't have a Galaxy password</strong> please go to <a href="${getAppRoot()}user/reset_password" target="_blank">this</a> page and request a password for your Galaxy account.`,
oidc_text: `<br/>If you are signed-in to Galaxy using a third-party identity and you <strong>do not have a Galaxy password</strong> please use the reset password option in the login form with your email to create a password for your account.`,
help_text: `This Galaxy server allows you to upload files via FTP. To upload some files, log in to the FTP server at <strong>${
options.ftp_upload_site
}</strong> using your Galaxy credentials.
@@ -435,6 +435,10 @@ var InputTerminal = BaseInputTerminal.extend({
}
if (thisMapOver.isCollection && thisMapOver.canMatch(otherCollectionType)) {
return this._producesAcceptableDatatype(other);
} else if (this.multiple && new CollectionTypeDescription('list').canMatch(otherCollectionType)) {
// This handles the special case of a list input being connected to a multiple="true" data input.
// Nested lists would be correctly mapped over by the above condition.
return this._producesAcceptableDatatype(other);
} else {
// Need to check if this would break constraints...
var mappingConstraints = this._mappingConstraints();
@@ -258,7 +258,7 @@ export default Backbone.View.extend({
for (let i = 0; i < workflow_versions.length; i++) {
const current_wf = workflow_versions[i];
let version_text = `Version ${current_wf["version"]}, ${current_wf["steps"]} steps`;
let version_text = `Version ${current_wf.version}, ${current_wf.steps} steps`;
let selected = false;
if (i == self.workflow.workflow_version) {
version_text = `${version_text} (active)`;
@@ -287,7 +287,9 @@ export default Backbone.View.extend({
$("#workflow-version-switch").unbind("change");
if (this.value != self.workflow.workflow_version) {
if (self.workflow && self.workflow.has_changes) {
const r = confirm("There are unsaved changes to your workflow which will be lost. Continue ?");
const r = window.confirm(
"There are unsaved changes to your workflow which will be lost. Continue ?"
);
if (r == false) {
// We rebuild the version select list, to reset the selected version
self.build_version_select();
@@ -774,6 +776,7 @@ export default Backbone.View.extend({
// Fix width to computed width
// Now add floats
var buttons = $("<div class='buttons' style='float: right;'></div>");
buttons.append($(`<div class="sr-only"><a href="#right">Node Details</a></div>`));
if (type !== "subworkflow") {
buttons.append(
$("<div/>")
+10
View File
@@ -60,6 +60,10 @@
}
}
}
tr.light {
font-weight: initial;
color: initial;
}
tr.light td {
background-color: white;
color: black;
@@ -75,6 +79,12 @@
background-color: #ebd4a4;
color: white;
}
tr.table-primary td {
color: $brand-light;
}
tr.table-primary td a {
color: $brand-white;
}
a.dark {
color: white;
}
+1 -11
View File
@@ -53,18 +53,8 @@ input[type="radio"] {
max-width: 900px; // BS4 sets this to 500px now, so width not respected.
}
.pagination {
> .active > a {
background-color: #ebd9b2;
color: black;
&:hover {
color: black;
background-color: #ebd9b2;
}
}
}
// Scroll modal body.
.modal-body {
overflow: auto;
}
+8
View File
@@ -162,6 +162,7 @@
<datatype extension="mz5" type="galaxy.datatypes.binary:H5" subclass="true" mimetype="application/octet-stream" display_in_upload="true"/>
<datatype extension="hivtrace" type="galaxy.datatypes.text:Json" mimetype="application/json" subclass="true" display_in_upload="false"/>
<datatype extension="cool" type="galaxy.datatypes.binary:Cool" mimetype="application/octet-stream" display_in_upload="true"/>
<datatype extension="mcool" type="galaxy.datatypes.binary:MCool" mimetype="application/octet-stream" display_in_upload="true"/>
<datatype extension="html" type="galaxy.datatypes.text:Html" mimetype="text/html"/>
<datatype extension="interval" type="galaxy.datatypes.interval:Interval" display_in_upload="true" description="File must start with definition line in the following format (columns may be in any order).">
<converter file="interval_to_bed_converter.xml" target_datatype="bed"/>
@@ -730,6 +731,12 @@
<datatype extension="sbml" type="galaxy.datatypes.xml:Sbml" mimetype="application/xml" display_in_upload="true"/>
<datatype extension="spalndbnp" type="galaxy.datatypes.spaln:SpalnNuclDb" display_in_upload="true" />
<datatype extension="spalndba" type="galaxy.datatypes.spaln:SpalnProtDb" display_in_upload="true" />
<datatype extension="dada2_derep" type="galaxy.datatypes.binary:RData" subclass="true" display_in_upload="true" />
<datatype extension="dada2_dada" type="galaxy.datatypes.binary:RData" subclass="true" display_in_upload="true" />
<datatype extension="dada2_errorrates" type="galaxy.datatypes.binary:RData" subclass="true" display_in_upload="true" />
<datatype extension="dada2_mergepairs" type="galaxy.datatypes.binary:RData" subclass="true" display_in_upload="true" />
<datatype extension="dada2_sequencetable" type="galaxy.datatypes.tabular:Tabular" mimetype="application/text" subclass="true" display_in_upload="true" />
<datatype extension="dada2_uniques" type="galaxy.datatypes.tabular:Tabular" mimetype="application/text" subclass="true" display_in_upload="true" />
</registration>
<sniffers>
<!--
@@ -769,6 +776,7 @@
<sniffer type="galaxy.datatypes.binary:GAFASQLite"/>
<sniffer type="galaxy.datatypes.binary:SQlite"/>
<sniffer type="galaxy.datatypes.binary:Cool"/>
<sniffer type="galaxy.datatypes.binary:MCool"/>
<sniffer type="galaxy.datatypes.binary:Loom"/>
<sniffer type="galaxy.datatypes.binary:Anndata"/>
<sniffer type="galaxy.datatypes.binary:Biom2"/>
+14 -10
View File
@@ -144,7 +144,7 @@ galaxy:
# than 5 milliseconds.
#slow_query_log_threshold: 0
# Enable's a per request sql debugging option. If this is set to true,
# Enables a per request sql debugging option. If this is set to true,
# append ?sql_debug=1 to web request URLs to enable detailed logging
# on the backend of SQL queries generated during that request. This is
# useful for debugging slow endpoints during development.
@@ -333,7 +333,7 @@ galaxy:
# be used for job destinations with Docker enabled.
#enable_beta_mulled_containers: false
# Container resolvers configuration (beta). Setup a file describing
# Container resolvers configuration (beta). Set up a file describing
# container resolvers to use when discovering containers for Galaxy.
# If this is set to None, the default containers loaded is determined
# by enable_beta_mulled_containers.
@@ -632,7 +632,7 @@ galaxy:
# Galaxy can display data at various external browsers. These options
# specify which browsers should be available. URLs and builds
# available at these browsers are defined in the specifield files. If
# available at these browsers are defined in the specified files. If
# use_remote_user = True, display application servers will be denied
# access to Galaxy and so displaying datasets in these sites will
# fail. display_servers contains a list of hostnames which should be
@@ -693,11 +693,10 @@ galaxy:
# URL (with schema http/https) of the Galaxy instance as accessible
# within your local network - if specified used as a default by pulsar
# file staging and Jupyter Docker container for communicating back
# with Galaxy via the API. If you are attempting to setup GIEs on Mac
# OS X with Docker for Mac - this should likely be the IP address of
# your machine on the virtualbox network (vboxnet0) setup for the
# Docker host VM. This can found by running ifconfig and using the IP
# address of the network vboxnet0.
# with Galaxy via the API. If you are attempting to set up GIEs on
# Mac OS X with Docker Desktop for Mac and your Galaxy instance runs
# on port 8080 this should be 'http://host.docker.internal:8080'. For
# more details see https://docs.docker.com/docker-for-mac/networking/
#galaxy_infrastructure_url: http://localhost:8080
# If the above URL cannot be determined ahead of time in dynamic
@@ -877,11 +876,11 @@ galaxy:
# for IPC, set that here.
#dynamic_proxy_session_map: database/session_map.sqlite
# Set the port and IP for the the dynamic proxy to bind to, this must
# Set the port and IP for the dynamic proxy to bind to, this must
# match the external configuration if dynamic_proxy_manage is False.
#dynamic_proxy_bind_port: 8800
# Set the port and IP for the the dynamic proxy to bind to, this must
# Set the port and IP for the dynamic proxy to bind to, this must
# match the external configuration if dynamic_proxy_manage is False.
#dynamic_proxy_bind_ip: 0.0.0.0
@@ -1530,6 +1529,11 @@ galaxy:
# configuration file.
#job_config_file: config/job_conf.xml
# Description of job running configuration, can be embedded into
# Galaxy configuration or loaded from an additional file with the
# job_config_file option.
#job_config: null
# When jobs fail due to job runner problems, Galaxy can be configured
# to retry these or reroute the jobs to new destinations. Very fine
# control of this is available with resubmit declarations in
+1 -1
View File
@@ -66,7 +66,7 @@
<!-- <param id="persistence_directory">/path/to/dir</param> -->
<!-- Number of seconds to wait for an acknowledgement before
republishing a message. -->
<!-- <param id="amqp_republish_time">30</param> -->
<!-- <param id="amqp_ack_republish_time">30</param> -->
<!-- Pulsar job manager to communicate with (see Pulsar
docs for information on job managers). -->
<!-- <param id="manager">_default_</param> -->
@@ -1,9 +0,0 @@
var IES = window.IES;
function load_askomics(url){
$( document ).ready(function() {
IES.test_ie_availability(url, function(){
IES.append_notebook(url);
});
});
}
@@ -0,0 +1,9 @@
var IES = window.IES;
function load_askomics(url) {
$(document).ready(function() {
IES.test_ie_availability(url, function() {
IES.append_notebook(url);
});
});
}
@@ -34,16 +34,13 @@ url = ie_request.url_template('${PROXY_URL}/login_api_gie?key=' + askomics_api_k
<html>
<head>
${ ie.load_default_js() }
${ ie.load_default_app() }
</head>
<body>
<script type="text/javascript">
${ ie.default_javascript_variables() }
var url = '${ url }';
${ ie.plugin_require_config() }
requirejs(['galaxy.interactive_environments', 'plugin/askomics'], function(IES){
window.IES = IES;
load_askomics(url);
});
load_askomics(url);
</script>
<div id="main" width="100%" height="100%">
</div>
@@ -2,20 +2,20 @@
var IES = window.IES;
var toastr = window.toastr;
function message_failed_auth(password){
toastr.info(
"Automatic authorization failed.",
"Please login manually",
{'closeButton': true, 'timeOut': 100000, 'tapToDismiss': false}
);
function message_failed_auth(password) {
toastr.info("Automatic authorization failed.", "Please login manually", {
closeButton: true,
timeOut: 100000,
tapToDismiss: false
});
}
function message_failed_connection(){
toastr.error(
"Could not connect to BAM iobio. Please contact your administrator.",
"Security warning",
{'closeButton': true, 'timeOut': 20000, 'tapToDismiss': true}
);
function message_failed_connection() {
toastr.error("Could not connect to BAM iobio. Please contact your administrator.", "Security warning", {
closeButton: true,
timeOut: 20000,
tapToDismiss: true
});
}
/**
@@ -25,10 +25,10 @@ function message_failed_connection(){
* @param {String} notebook_access_url: the URL embeded in the page and loaded
*
*/
function load_notebook(notebook_access_url){
function load_notebook(notebook_access_url) {
// Test notebook_login_url for accessibility, executing the login+load function whenever
// we've successfully connected to the IE.
IES.test_ie_availability(notebook_access_url, function(){
IES.test_ie_availability(notebook_access_url, function() {
_handle_notebook_loading(notebook_access_url);
});
}
@@ -36,6 +36,6 @@ function load_notebook(notebook_access_url){
/**
* Must be implemented by IEs
*/
function _handle_notebook_loading(notebook_access_url){
function _handle_notebook_loading(notebook_access_url) {
IES.append_notebook(notebook_access_url);
}
@@ -24,6 +24,7 @@ root = h.url_for( '/' )
<html>
<head>
${ ie.load_default_js() }
${ ie.load_default_app() }
</head>
<body>
@@ -31,12 +32,8 @@ root = h.url_for( '/' )
${ ie.default_javascript_variables() }
var notebook_access_url = '${ notebook_access_url }';
${ ie.plugin_require_config() }
requirejs(['galaxy.interactive_environments', 'plugin/bam_iobio'], function(IES){
window.IES = IES;
IES.display_spinner();
});
IES.display_spinner();
toastr.info(
"BAM io.bio is starting up!",
@@ -46,11 +43,8 @@ root = h.url_for( '/' )
var startup = function(){
// Load notebook
requirejs(['galaxy.interactive_environments', 'plugin/bam_iobio'], function(IES){
window.IES = IES;
IES.load_when_ready(ie_readiness_url, function(){
load_notebook(notebook_access_url);
});
IES.load_when_ready(ie_readiness_url, function(){
load_notebook(notebook_access_url);
});
};
@@ -1,5 +1,5 @@
---
-
image: quay.io/galaxy/cellxgene-galaxy-ie:2019.01.30
image: quay.io/galaxy/cellxgene-galaxy-ie:2019.05.13
description: |
An interactive data explorer for single-cell transcriptomics datasets
@@ -1,6 +1,10 @@
FROM python:3.6.8-slim
RUN apt-get update && apt-get install -y \
net-tools \
&& rm -rf /var/lib/apt/lists/*
RUN pip3 install cellxgene && rm -rf ~/.cache
RUN pip3 install cellxgene galaxy-ie-helpers && rm -rf ~/.cache
ADD ./run_cellxgene.sh /
@@ -1,3 +1,5 @@
#!/bin/sh
cellxgene launch --host 0.0.0.0 --port 80 /input/file.*
get -i ${DATASET_HID} \
&& mv /import/${DATASET_HID} /import/${DATASET_HID}.h5ad \
&& cellxgene launch --host 0.0.0.0 --port 80 /import/${DATASET_HID}.h5ad
@@ -2,12 +2,11 @@
<%
ie_request.load_deploy_config()
# Make the dataset available inside the container
data_file = ie_request.volume('/input/file.' + hda.ext, hda.file_name, mode='ro')
ie_request.launch(
image = trans.request.params.get('image_tag', None),
volumes = [data_file]
image = trans.request.params.get('image_tag', None),
env_override={
'dataset_hid': hda.hid
}
)
url = ie_request.url_template('${PROXY_URL}')
@@ -21,14 +20,11 @@
${ ie.default_javascript_variables() }
var url = '${ url }';
${ ie.plugin_require_config() }
requirejs(['galaxy.interactive_environments'], function (IES) {
$( document ).ready(function() {
IES.keepAlive(url);
IES.test_ie_availability(url, function() {
IES.append_notebook(url);
});
$( document ).ready(function() {
IES.keepAlive(url);
IES.test_ie_availability(url, function() {
IES.append_notebook(url);
});
});
@@ -1,3 +1,10 @@
<%def name="load_default_js()">
${h.css( 'base' ) }
${h.js('bundled/libs.chunk',
'bundled/base.chunk',
'bundled/generic.bundled')}
</%def>
<%def name="default_javascript_variables()">
// Globals
@@ -10,28 +17,15 @@ ie_password = '${ ie_request.notebook_pw }';
var galaxy_root = '${ ie_request.attr.root }';
var app_root = '${ ie_request.attr.app_root }';
var ie_readiness_url = '${ h.url_for("/interactive_environments/ready") }';
</%def>
<%def name="load_default_js()">
${h.css( 'base' ) }
${h.js( 'libs/jquery/jquery',
'libs/toastr',
'libs/require')}
</%def>
<%def name="plugin_require_config()">
require.config({
baseUrl: app_root,
paths: {
"plugin" : app_root + "js",
"galaxy.interactive_environments": "${h.url_for('/static/scripts/galaxy.interactive_environments')}",
},
urlArgs: "v=${app.server_starttime}",
});
window.IES = bundleEntries.IES;
window.toastr = bundleEntries.Toast;
window.onbeforeunload = function() {
return 'You are leaving your Interactive Environment.';
};
</%def>
<%def name="load_default_app()">
<script src="${'%sjs/main.js?v=%s' % (ie_request.attr.app_root, app.server_starttime)}" type="text/javascript"></script>
</%def>
@@ -2,8 +2,8 @@
var IES = window.IES;
var toastr = window.toastr;
function load_notebook(url){
IES.test_ie_availability(url, function(){
function load_notebook(url) {
IES.test_ie_availability(url, function() {
IES.append_notebook(url);
});
}
@@ -18,20 +18,17 @@ url = ie_request.url_template('${PROXY_URL}/ethercalc/')
<html>
<head>
${ ie.load_default_js() }
${ ie.load_default_app() }
</head>
<body>
<script type="text/javascript">
${ ie.default_javascript_variables() }
var url = '${ url }';
${ ie.plugin_require_config() }
// Keep container running and load IE
requirejs(['galaxy.interactive_environments', 'plugin/ethercalc'], function(IES){
window.IES = IES;
IES.keepAlive(url);
IES.load_when_ready(ie_readiness_url, function(){
load_notebook(url);
});
IES.keepAlive(url);
IES.load_when_ready(ie_readiness_url, function(){
load_notebook(url);
});
</script>
<div id="main" width="100%" height="100%">
@@ -3,12 +3,12 @@ var IES = window.IES;
// Load an interactive environment (IE) from a remote URL
// @param {String} hicexplorer_access_url: the URL embeded in the page and loaded
function load_hicexplorer(hicexplorer_access_url){
function load_hicexplorer(hicexplorer_access_url) {
// When the page has completely loaded...
$( document ).ready(function() {
$(document).ready(function() {
// Test if we can access the GIE, and if so, execute the function
// to load the GIE for the user.
IES.test_ie_availability(hicexplorer_access_url, function(){
IES.test_ie_availability(hicexplorer_access_url, function() {
IES.append_notebook(hicexplorer_access_url);
});
});
@@ -29,19 +29,14 @@
<html>
<head>
${ ie.load_default_js() }
${ ie.load_default_app() }
</head>
<body>
<script type="text/javascript">
${ ie.default_javascript_variables() }
var url = '${ url }';
${ ie.plugin_require_config() }
requirejs(['galaxy.interactive_environments', 'plugin/hicbrowser'], function (IES) {
window.IES = IES;
load_hicexplorer(url);
});
load_hicexplorer(url);
</script>
<div id="main" width="100%" height="100%">
@@ -2,23 +2,25 @@
var IES = window.IES;
var toastr = window.toastr;
function message_failed_auth(password){
function message_failed_auth(password) {
toastr.info(
"Automatic authorization failed. You can manually login with:<br>" + password + "<br> <a href='https://github.com/bgruening/galaxy-ipython/wiki/Automatic-Authorization-Failed' target='_blank'>More details ...</a>",
"Automatic authorization failed. You can manually login with:<br>" +
password +
"<br> <a href='https://github.com/bgruening/galaxy-ipython/wiki/Automatic-Authorization-Failed' target='_blank'>More details ...</a>",
"Please login manually",
{'closeButton': true, 'timeOut': 100000, 'tapToDismiss': false}
{ closeButton: true, timeOut: 100000, tapToDismiss: false }
);
}
function message_failed_connection(){
function message_failed_connection() {
toastr.error(
"Could not connect to Jupyter Notebook. Please contact your administrator. <a href='https://github.com/bgruening/galaxy-ipython/wiki/Could-not-connect-to-IPython-Notebook' target='_blank'>More details ...</a>",
"Security warning",
{'closeButton': true, 'timeOut': 20000, 'tapToDismiss': true}
"Security warning",
{ closeButton: true, timeOut: 20000, tapToDismiss: true }
);
}
function message_no_auth(){
function message_no_auth() {
// No longer a security issue, proxy validates Galaxy session token.
/*
toastr.warning(
@@ -29,7 +31,6 @@ function message_no_auth(){
*/
}
/**
* Load an interactive environment (IE) from a remote URL
* @param {String} password: password used to authenticate to the remote resource
@@ -37,68 +38,82 @@ function message_no_auth(){
* @param {String} notebook_access_url: the URL embeded in the page and loaded
*
*/
function load_notebook(password, notebook_login_url, notebook_access_url){
function load_notebook(password, notebook_login_url, notebook_access_url) {
// Test notebook_login_url for accessibility, executing the login+load function whenever
// we've successfully connected to the IE.
IES.test_ie_availability(notebook_login_url, function(){
IES.test_ie_availability(notebook_login_url, function() {
_handle_notebook_loading(password, notebook_login_url, notebook_access_url);
keep_alive();
});
}
function keep_alive(){
function keep_alive() {
/**
* This is needed to keep the container alive. If the user leaves this site
* this function is not constantly pinging the container, the container will
* terminate itself.
*/
* This is needed to keep the container alive. If the user leaves this site
* this function is not constantly pinging the container, the container will
* terminate itself.
*/
var warn_at = 4;
var count_max = 60;
// we sleep 15 seconds between requests and the default timeout for the Jupyter container is 120 seconds, so start
// with a pretty high ajax timeout. sleep starts low because we want to get the warning up pretty quickly
var spin_state = IES.make_spin_state("IE keepalive", 8000, 16000, 2000, 5000, 15000, 5000, false);
var success = function(){
var success = function() {
console.log("IE keepalive request succeeded");
toastr.clear();
if(spin_state.count >= warn_at){
if (spin_state.count >= warn_at) {
toastr.clear();
toastr.success(
"Interactive environment connection restored",
{'closeButton': true, 'timeOut': 5000, 'extendedTimeOut': 2000, 'tapToDismiss': true}
);
toastr.success("Interactive environment connection restored", {
closeButton: true,
timeOut: 5000,
extendedTimeOut: 2000,
tapToDismiss: true
});
}
spin_state.count = 0;
spin_state.timeout_count = 0;
spin_state.error_count = 0;
return false; // keep spinning
return false; // keep spinning
};
var timeout_error = function(jqxhr, status, error){
console.log("IE keepalive request failed " + spin_state.count + " time(s) of " + count_max + " max: " + status + ": " + error);
if(spin_state.count == warn_at){
var timeout_error = function(jqxhr, status, error) {
console.log(
"IE keepalive request failed " +
spin_state.count +
" time(s) of " +
count_max +
" max: " +
status +
": " +
error
);
if (spin_state.count == warn_at) {
toastr.warning(
"Your browser has been unable to contact the interactive environment for "
+ spin_state.count + " consecutive attempts, if you do not reestablish "
+ "a connection, your IE container may be terminated.",
"Your browser has been unable to contact the interactive environment for " +
spin_state.count +
" consecutive attempts, if you do not reestablish " +
"a connection, your IE container may be terminated.",
"Warning",
{'closeButton': true, 'timeOut': 0, 'extendedTimeOut': 0, 'tapToDismiss': false}
{ closeButton: true, timeOut: 0, extendedTimeOut: 0, tapToDismiss: false }
);
return false; // keep spinning
}else if(spin_state.count >= count_max){
IES.spin_error("IE keepalive failure limit reached", "Lost connection to interactive environment, contact your administrator", false);
return true; // stop spinning
return false; // keep spinning
} else if (spin_state.count >= count_max) {
IES.spin_error(
"IE keepalive failure limit reached",
"Lost connection to interactive environment, contact your administrator",
false
);
return true; // stop spinning
}
};
console.log("IE keepalive worker starting");
IES.spin(notebook_keepalive_url, false, success, timeout_error, timeout_error, spin_state);
}
/**
* Must be implemented by IEs
*/
function _handle_notebook_loading(password, notebook_login_url, notebook_access_url){
if ( ie_password_auth ) {
function _handle_notebook_loading(password, notebook_login_url, notebook_access_url) {
if (ie_password_auth) {
// Make an AJAX POST
$.ajax({
type: "POST",
@@ -106,29 +121,28 @@ function _handle_notebook_loading(password, notebook_login_url, notebook_access_
url: notebook_login_url,
// With our password
data: {
'password': password
password: password
},
xhrFields: {
withCredentials: true
},
// If that is successful, load the notebook
success: function(){
success: function() {
IES.append_notebook(notebook_access_url);
},
error: function(jqxhr, status, error){
if(ie_password_auth){
error: function(jqxhr, status, error) {
if (ie_password_auth) {
// Failure happens due to CORS
message_failed_auth(password);
IES.append_notebook(notebook_access_url);
}else{
} else {
message_failed_connection();
// Do we want to try and load the notebook anyway? Just in case?
IES.append_notebook(notebook_access_url);
}
}
});
}
else {
} else {
// Not using password auth, just embed it to avoid content-origin issues.
message_no_auth();
IES.append_notebook(notebook_access_url);
@@ -50,6 +50,7 @@ notebook_keepalive_url = ie_request.url_template('${PROXY_URL}/ipython/tree')
<html>
<head>
${ ie.load_default_js() }
${ ie.load_default_app() }
</head>
<body>
@@ -58,16 +59,10 @@ ${ ie.default_javascript_variables() }
var notebook_login_url = '${ notebook_login_url }';
var notebook_access_url = '${ notebook_access_url }';
var notebook_keepalive_url = '${ notebook_keepalive_url }';
${ ie.plugin_require_config() }
// Load notebook
requirejs(['galaxy.interactive_environments', 'plugin/jupyter'], function(IES){
// This global is not awesome, get rid of it when possible (when IES are a part of the build process)
window.IES = IES;
IES.load_when_ready(ie_readiness_url, function(){
load_notebook(ie_password, notebook_login_url, notebook_access_url);
});
IES.load_when_ready(ie_readiness_url, function(){
load_notebook(ie_password, notebook_login_url, notebook_access_url);
});
@@ -3,10 +3,10 @@ var IES = window.IES;
// Load an interactive environment (IE) from a remote URL
// @param {String} notebook_access_url: the URL embeded in the page and loaded
function load_notebook(notebook_access_url){
function load_notebook(notebook_access_url) {
// Test if we can access the GIE, and if so, execute the function
// to load the GIE for the user.
IES.test_ie_availability(notebook_access_url, function(){
IES.test_ie_availability(notebook_access_url, function() {
IES.append_notebook(notebook_access_url);
});
}
@@ -23,20 +23,14 @@
<html>
<head>
${ ie.load_default_js() }
${ ie.load_default_app() }
</head>
<body>
<script type="text/javascript">
${ ie.default_javascript_variables() }
${ ie.default_javascript_variables() }
var url = '${ url }';
${ ie.plugin_require_config() }
requirejs(['galaxy.interactive_environments', 'plugin/neo'], function (IES) {
window.IES = IES;
IES.load_when_ready(ie_readiness_url, function(){
load_notebook(url);
});
IES.load_when_ready(ie_readiness_url, function(){
load_notebook(url);
});
</script>
@@ -0,0 +1,54 @@
// Globals to be rid of
var IES = window.IES;
var toastr = window.toastr;
function load_notebook(url) {
IES.test_ie_availability(url, function() {
append_notebook(url);
});
}
function append_notebook(url) {
IES.clear_main_area();
$("#main").append(
'<iframe frameBorder="0" seamless="seamless" style="width: 100%; height: 100%; overflow:auto;" scrolling="yes" src="' +
url +
'"></iframe>'
);
}
function keep_alive(notebook_access_url) {
/**
* This is needed to keep the container alive. If the user leaves this site
* this function is not constantly pinging the container, the container will
* terminate itself.
*/
var request_count = 0;
var interval = window.setInterval(function() {
$.ajax({
url: notebook_access_url,
xhrFields: {
withCredentials: true
},
type: "GET",
timeout: 500,
success: function() {
console.log("Connected to IE, returning");
},
error: function(jqxhr, status, error) {
request_count++;
console.log("Request " + request_count);
if (request_count > 30) {
window.clearInterval(interval);
IES.clear_main_area();
toastr.error("Could not connect to IE, contact your administrator", "Error", {
closeButton: true,
timeOut: 20000,
tapToDismiss: false
});
}
}
});
}, 10000);
}
@@ -1,54 +0,0 @@
// Globals to be rid of
var IES = window.IES;
var toastr = window.toastr;
function load_notebook(url){
IES.test_ie_availability(url, function(){
append_notebook(url);
});
}
function append_notebook(url){
IES.clear_main_area();
$('#main').append(
'<iframe frameBorder="0" seamless="seamless" style="width: 100%; height: 100%; overflow:auto;" scrolling="yes" src="'+ url +'"></iframe>'
);
}
function keep_alive(notebook_access_url){
/**
* This is needed to keep the container alive. If the user leaves this site
* this function is not constantly pinging the container, the container will
* terminate itself.
*/
var request_count = 0;
var interval = window.setInterval(function(){
$.ajax({
url: notebook_access_url,
xhrFields: {
withCredentials: true
},
type: "GET",
timeout: 500,
success: function(){
console.log("Connected to IE, returning");
},
error: function(jqxhr, status, error){
request_count++;
console.log("Request " + request_count);
if(request_count > 30){
window.clearInterval(interval);
IES.clear_main_area();
toastr.error(
"Could not connect to IE, contact your administrator",
"Error",
{'closeButton': true, 'timeOut': 20000, 'tapToDismiss': false}
);
}
}
});
}, 10000);
}
@@ -21,21 +21,18 @@ url = ie_request.url_template('${PROXY_URL}/phinch/')
%>
<html>
<head>
${ ie.load_default_js() }
${ ie.load_default_js() }
${ ie.load_default_app() }
</head>
<body>
<script type="text/javascript">
${ ie.default_javascript_variables() }
var url = '${ url }';
${ ie.plugin_require_config() }
// Keep container running and load notebook
requirejs(['galaxy.interactive_environments', 'plugin/phinch'], function(IES){
window.IES = IES;
keep_alive(url);
IES.load_when_ready(ie_readiness_url, function(){
load_notebook(url);
});
keep_alive(url);
IES.load_when_ready(ie_readiness_url, function(){
load_notebook(url);
});
</script>
@@ -6,7 +6,7 @@
# appropriate `apt-get/pip install` statements.
---
-
image: erasche/docker-rstudio-notebook:19.05
image: quay.io/erasche/docker-rstudio-notebook:19.05
description: |
This familiar R analysis software suite will let you explore your
datasets in depth. Comes with ggplot2, RODBC, maps, shinyapps, knitr,
@@ -0,0 +1,24 @@
// Assumed globals we need to get rid of
var IES = window.IES;
var toastr = window.toastr;
function message_failed_connection() {
toastr.error("Could not connect to RStudio. Please contact your administrator.", "Security warning", {
closeButton: true,
timeOut: 20000,
tapToDismiss: true
});
}
/**
* Load an interactive environment (IE) from a remote URL
* @param {String} notebook_access_url: the URL embeded in the page and loaded
*
*/
function load_notebook(notebook_access_url) {
// Test notebook_login_url for accessibility, executing the login+load function whenever
// we've successfully connected to the IE.
IES.test_ie_availability(notebook_access_url, function() {
IES.append_notebook(notebook_access_url);
});
}
@@ -1,32 +0,0 @@
// Assumed globals we need to get rid of
var IES = window.IES;
var toastr = window.toastr;
function message_failed_connection(){
toastr.error(
"Could not connect to RStudio. Please contact your administrator.",
"Security warning",
{'closeButton': true, 'timeOut': 20000, 'tapToDismiss': true}
);
}
/**
* Load an interactive environment (IE) from a remote URL
* @param {String} notebook_access_url: the URL embeded in the page and loaded
*
*/
function load_notebook(notebook_access_url){
// Test notebook_login_url for accessibility, executing the login+load function whenever
// we've successfully connected to the IE.
IES.test_ie_availability(notebook_access_url, function(){
$.ajax({
type: 'GET',
url: notebook_access_url,
success: function(response_text){
IES.append_notebook(notebook_access_url);
}
});
});
}
@@ -27,18 +27,15 @@ notebook_access_url = ie_request.url_template('${PROXY_URL}/rstudio/')
<html>
<head>
${ ie.load_default_js() }
${ ie.load_default_app() }
</head>
<body style="margin: 0px">
<script type="text/javascript">
${ ie.default_javascript_variables() }
var notebook_access_url = '${ notebook_access_url }';
${ ie.plugin_require_config() }
requirejs(['galaxy.interactive_environments', 'plugin/rstudio'], function(IES){
window.IES = IES;
IES.load_when_ready(ie_readiness_url, function(){
load_notebook(notebook_access_url);
});
IES.load_when_ready(ie_readiness_url, function(){
load_notebook(notebook_access_url);
});
</script>
<div id="main" width="100%" height="100%">
+21 -5
View File
@@ -393,15 +393,31 @@ Gitter or IRC channel.
17. How can I upgrade Conda?
****************************
Many potential issues with Conda have been resolved with fixes in Conda itself. If
you let Galaxy install Conda prior to the release of 17.01 you probably have version
3.19.3. This can be updated to 4.2.13 with the following command:
Many potential issues with Conda have been resolved with fixes in Conda itself.
The Conda installed by Galaxy can be updated to e.g. version 4.6.14 with the
following command:
.. code-block:: bash
$ <tool_dependency_dir/_conda/bin/conda update -y conda==4.2.13
$ <tool_dependency_dir/_conda/bin/conda install conda==4.6.14
The command can obviously be adapted to install any version of Conda.
The command can obviously be adapted to install any version of Conda. If the
above command fails with an error like:
.. code-block:: bash
UnsatisfiableError: The following specifications were found to be in conflict:
- conda ==4.6.14 -> python >=3.6,<3.7.0a0
- python 3.5*
Use "conda info <package>" to see the dependencies for each package.
Then you need to also update the ``python`` package installed in the base
environment by appending to the ``conda install`` command above an appropriate
specification, which for the example error above would be ``python==3.6``:
.. code-block:: bash
$ <tool_dependency_dir/_conda/bin/conda install conda==4.6.14 python==3.6
.. _Conda documentation: https://conda.io/docs/
+24 -14
View File
@@ -105,7 +105,7 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:Description:
Enable's a per request sql debugging option. If this is set to
Enables a per request sql debugging option. If this is set to
true, append ?sql_debug=1 to web request URLs to enable detailed
logging on the backend of SQL queries generated during that
request. This is useful for debugging slow endpoints during
@@ -517,7 +517,7 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:Description:
Container resolvers configuration (beta). Setup a file describing
Container resolvers configuration (beta). Set up a file describing
container resolvers to use when discovering containers for Galaxy.
If this is set to None, the default containers loaded is
determined by enable_beta_mulled_containers.
@@ -1175,7 +1175,7 @@
:Description:
Galaxy can display data at various external browsers. These
options specify which browsers should be available. URLs and
builds available at these browsers are defined in the specifield
builds available at these browsers are defined in the specified
files. If use_remote_user = True, display application servers
will be denied access to Galaxy and so displaying datasets in
these sites will fail. display_servers contains a list of
@@ -1301,11 +1301,11 @@
URL (with schema http/https) of the Galaxy instance as accessible
within your local network - if specified used as a default by
pulsar file staging and Jupyter Docker container for communicating
back with Galaxy via the API. If you are attempting to setup GIEs
on Mac OS X with Docker for Mac - this should likely be the IP
address of your machine on the virtualbox network (vboxnet0) setup
for the Docker host VM. This can found by running ifconfig and
using the IP address of the network vboxnet0.
back with Galaxy via the API. If you are attempting to set up
GIEs on Mac OS X with Docker Desktop for Mac and your Galaxy
instance runs on port 8080 this should be
'http://host.docker.internal:8080'. For more details see
https://docs.docker.com/docker-for-mac/networking/
:Default: ``http://localhost:8080``
:Type: str
@@ -1732,9 +1732,8 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~
:Description:
Set the port and IP for the the dynamic proxy to bind to, this
must match the external configuration if dynamic_proxy_manage is
False.
Set the port and IP for the dynamic proxy to bind to, this must
match the external configuration if dynamic_proxy_manage is False.
:Default: ``8800``
:Type: int
@@ -1744,9 +1743,8 @@
~~~~~~~~~~~~~~~~~~~~~~~~~
:Description:
Set the port and IP for the the dynamic proxy to bind to, this
must match the external configuration if dynamic_proxy_manage is
False.
Set the port and IP for the dynamic proxy to bind to, this must
match the external configuration if dynamic_proxy_manage is False.
:Default: ``0.0.0.0``
:Type: str
@@ -3187,6 +3185,18 @@
:Type: str
~~~~~~~~~~~~~~
``job_config``
~~~~~~~~~~~~~~
:Description:
Description of job running configuration, can be embedded into
Galaxy configuration or loaded from an additional file with the
job_config_file option.
:Default: ``None``
:Type: map
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``default_job_resubmission_condition``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -36,17 +36,13 @@ and `Ansible Galaxy <https://galaxy.ansible.com/detail#/role/6056>`__.
Setting up the Proxy
^^^^^^^^^^^^^^^^^^^^
Currently the Galaxy proxy is a NodeJS+Sqlite3 proxy.
The Galaxy IE Proxy is a NodeJS+Sqlite3 application. The NodeJS that is
installed by default into the Galaxy Virtualenv is suitable for an execution
environment for the Galaxy IE Proxy.
- Node has recently upgraded, and our proxy is pinned to an old version of
sqlite3. As such you'll currently need to have an older version of Node
available (0.10.X - 0.11.X vintage).
- One can use `NVM <https://github.com/creationix/nvm>`__ to install and manage multiple versions of Node.
- We're working on solutions in this space to provide a better deployment
mechanism here and fewer dependencies.
- Please note that if you have NodeJS installed under Ubuntu, it often
installs to ``/usr/bin/nodejs``, whereas ``npm`` expects it to be
``/usr/bin/node``. You will need to create that symlink yourself.
- Note that if you have NodeJS installed under Ubuntu, it often installs to
``/usr/bin/nodejs``, whereas ``npm`` expects it to be ``/usr/bin/node``. You
may need to create that symlink yourself.
Once Node and npm are ready to go, you'll need to install the dependencies
+7 -13
View File
@@ -205,9 +205,10 @@ We'll continue appending to our ``helloworld.mako`` the HTML code that's actuall
<html>
<head>
<!-- Loads some necessary javascript libraries. Specifically jquery,
toastr, and requirejs -->
<!-- Loads core Galaxy javascript libraries for a 'generic' entrypoint. -->
${ ie.load_default_js() }
<!-- This will load code from static/main.js, often used to handle things like login -->
${ ie.load_default_app() }
</head>
<body>
@@ -217,18 +218,11 @@ We'll continue appending to our ``helloworld.mako`` the HTML code that's actuall
var notebook_login_url = 'unused';
var notebook_access_url = '${ notebook_access_url }';
// Load code with require.js
${ ie.plugin_require_config() }
// Load notebook
// This will load code from static/helloworld.js, often used to handle
// things like Login. The load_notebook function will eventually append
// an IFrame to the <div id="main" /> below.
requirejs(['galaxy.interactive_environments', 'plugin/helloworld'], function(IES){
window.IES = IES
IES.load_when_ready(ie_readiness_url, function(){
load_notebook(notebook_access_url);
});
// The load_notebook function will eventually append an IFrame to the <div id="main" /> below.
IES.load_when_ready(ie_readiness_url, function(){
load_notebook(notebook_access_url);
});
</script>
<div id="main" width="100%" height="100%">
@@ -262,7 +256,7 @@ authentication of users, so you don't have to worry about it, and can just
assume that only the correct user will have access to a given notebook.
In the ``static/`` directory, we generally create a ``js/`` directory below that,
and create a ``{gie}.js`` (so, ``static/js/helloworld.js``) file in there.
and create a ``main.js`` file in there.
That file will have a function, ``load_notebook`` which will check if the GIE is available,
and when it is, display it to the user.
+60
View File
@@ -1107,6 +1107,66 @@ class Cool(H5):
return "Cool (HDF5) file (%s)." % (nice_size(dataset.get_size()))
class MCool(H5):
"""
Class describing the multi-resolution cool format (https://github.com/mirnylab/cooler)
"""
file_ext = "mcool"
def sniff(self, filename):
"""
>>> from galaxy.datatypes.sniff import get_test_fname
>>> fname = get_test_fname('matrix.mcool')
>>> MCool().sniff(fname)
True
>>> fname = get_test_fname('matrix.cool')
>>> MCool().sniff(fname)
False
>>> fname = get_test_fname('test.mz5')
>>> MCool().sniff(fname)
False
>>> fname = get_test_fname('wiggle.wig')
>>> MCool().sniff(fname)
False
>>> fname = get_test_fname('biom2_sparse_otu_table_hdf5.biom2')
>>> MCool().sniff(fname)
False
"""
MAGIC = "HDF5::Cooler"
URL = "https://github.com/mirnylab/cooler"
if super(MCool, self).sniff(filename):
keys0 = ['resolutions']
with h5py.File(filename, 'r') as handle:
if not all(name in handle.keys() for name in keys0):
return False
res0 = list(handle['resolutions'].keys())[0]
keys = ['chroms', 'bins', 'pixels', 'indexes']
fmt = handle['resolutions'][res0].attrs.get('format', None)
url = handle['resolutions'][res0].attrs.get('format-url', None)
if fmt == MAGIC or url == URL:
if not all(name in handle['resolutions'][res0].keys() for name in keys):
return False
return True
return False
def set_peek(self, dataset, is_multi_byte=False):
if not dataset.dataset.purged:
dataset.peek = "Multi-resolution Cool (HDF5) file for storing genomic interaction data."
dataset.blurb = nice_size(dataset.get_size())
else:
dataset.peek = 'file does not exist'
dataset.blurb = 'file purged from disk'
def display_peek(self, dataset):
try:
return dataset.peek
except Exception:
return "MCool (HDF5) file (%s)." % (nice_size(dataset.get_size()))
class Scf(Binary):
"""Class describing an scf binary sequence file"""
edam_format = "format_1632"
+24 -11
View File
@@ -1030,8 +1030,14 @@ def get_file_peek(file_name, is_multi_byte=False, WIDTH=256, LINE_COUNT=5, skipc
:param is_multi_byte: deprecated
:type is_multi_byte: bool
>>> fname = get_test_fname('4.bed')
>>> assert get_file_peek(fname, LINE_COUNT=1) == u'chr22\\t30128507\\t31828507\\tuc003bnx.1_cds_2_0_chr22_29227_f\\t0\\t+\\n'
>>> def assert_peek_is(file_name, expected, *args, **kwd):
... path = get_test_fname(file_name)
... peek = get_file_peek(path, *args, **kwd)
... assert peek == expected, "%s != %s" % (peek, expected)
>>> assert_peek_is('0_nonewline', u'0')
>>> assert_peek_is('0.txt', u'0\\n')
>>> assert_peek_is('4.bed', u'chr22\\t30128507\\t31828507\\tuc003bnx.1_cds_2_0_chr22_29227_f\\t0\\t+\\n', LINE_COUNT=1)
>>> assert_peek_is('1.bed', u'chr1\\t147962192\\t147962580\\tCCDS989.1_cds_0_0_chr1_147962193_r\\t0\\t-\\nchr1\\t147984545\\t147984630\\tCCDS990.1_cds_0_0_chr1_147984546_f\\t0\\t+\\n', LINE_COUNT=2)
"""
# Set size for file.readline() to a negative number to force it to
# read until either a newline or EOF. Needed for datasets with very
@@ -1042,20 +1048,27 @@ def get_file_peek(file_name, is_multi_byte=False, WIDTH=256, LINE_COUNT=5, skipc
skipchars = []
lines = []
count = 0
last_line_break = False
with compression_utils.get_fileobj(file_name, "U") as temp:
while count < LINE_COUNT:
try:
line = temp.readline(WIDTH)
except UnicodeDecodeError:
return "binary file"
if not line_wrap:
if line.endswith('\n'):
line = line[:-1]
else:
while True:
i = temp.read(1)
if not i or i == '\n':
break
if line == "":
break
last_line_break = False
if line.endswith('\n'):
line = line[:-1]
last_line_break = True
elif not line_wrap:
while True:
i = temp.read(1)
if i == '\n':
last_line_break = True
if not i or i == '\n':
break
skip_line = False
for skipchar in skipchars:
if line.startswith(skipchar):
@@ -1064,4 +1077,4 @@ def get_file_peek(file_name, is_multi_byte=False, WIDTH=256, LINE_COUNT=5, skipc
if not skip_line:
lines.append(line)
count += 1
return '\n'.join(lines)
return '\n'.join(lines) + ('\n' if last_line_break else '')
+52 -78
View File
@@ -14,7 +14,11 @@ import sys
import tempfile
import zipfile
from six import StringIO, text_type
from six import (
PY3,
StringIO,
text_type,
)
from six.moves import filter
from six.moves.urllib.request import urlopen
@@ -103,28 +107,40 @@ def stream_to_file(stream, suffix='', prefix='', dir=None, text=False, **kwd):
return stream_to_open_named_file(stream, fd, temp_name, **kwd)
def convert_newlines(fname, in_place=True, tmp_dir=None, tmp_prefix="gxupload"):
def convert_newlines(fname, in_place=True, tmp_dir=None, tmp_prefix="gxupload", block_size=128 * 1024, regexp=None):
"""
Converts in place a file from universal line endings
to Posix line endings.
>>> fname = get_test_fname('temp.txt')
>>> with open(fname, 'wt') as fh:
... _ = fh.write("1 2\\r3 4")
>>> convert_newlines(fname, tmp_prefix="gxtest", tmp_dir=tempfile.gettempdir())
(2, None)
>>> open(fname).read()
'1 2\\n3 4\\n'
"""
fd, temp_name = tempfile.mkstemp(prefix=tmp_prefix, dir=tmp_dir)
with io.open(fd, mode="wt", encoding='utf-8') as fp:
i = None
for i, line in enumerate(io.open(fname, encoding='utf-8')):
fp.write("%s\n" % line.rstrip("\r\n"))
if i is None:
i = 0
i = 0
if PY3:
NEWLINE_BYTE = 10
CR_BYTE = 13
else:
i += 1
NEWLINE_BYTE = "\n"
CR_BYTE = "\r"
with io.open(fd, mode="wb") as fp, io.open(fname, mode="rb") as fi:
last_char = None
block = fi.read(block_size)
last_block = b""
while block:
if last_char == CR_BYTE and block.startswith(b"\n"):
# Last block ended with CR, new block startswith newline.
# Since we replace CR with newline in the previous iteration we skip the first byte
block = block[1:]
if block:
last_char = block[-1]
block = block.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
if regexp:
block = b"\t".join(regexp.split(block))
fp.write(block)
i += block.count(b"\n")
last_block = block
block = fi.read(block_size)
if last_block and last_block[-1] != NEWLINE_BYTE:
i += 1
fp.write(b"\n")
if in_place:
shutil.move(temp_name, fname)
# Return number of lines in file.
@@ -133,47 +149,9 @@ def convert_newlines(fname, in_place=True, tmp_dir=None, tmp_prefix="gxupload"):
return (i, temp_name)
def sep2tabs(fname, in_place=True, patt=r"\s+", tmp_dir=None, tmp_prefix="gxupload"):
def convert_newlines_sep2tabs(fname, in_place=True, patt=br"[^\S\n]+", tmp_dir=None, tmp_prefix="gxupload"):
"""
Transforms in place a 'sep' separated file to a tab separated one
>>> fname = get_test_fname('temp.txt')
>>> with open(fname, 'wt') as fh:
... _ = fh.write(u"1 2\\n3 4\\n")
>>> sep2tabs(fname)
(2, None)
>>> open(fname).read()
'1\\t2\\n3\\t4\\n'
"""
regexp = re.compile(patt)
fd, temp_name = tempfile.mkstemp(prefix=tmp_prefix, dir=tmp_dir)
with io.open(fd, mode="wt", encoding='utf-8') as fp:
i = None
for i, line in enumerate(io.open(fname, encoding='utf-8')):
if line.endswith("\r"):
line = line.rstrip('\r')
elems = regexp.split(line)
fp.write(u"%s\r" % '\t'.join(elems))
else:
line = line.rstrip('\n')
elems = regexp.split(line)
fp.write(u"%s\n" % '\t'.join(elems))
if i is None:
i = 0
else:
i += 1
if in_place:
shutil.move(temp_name, fname)
# Return number of lines in file.
return (i, None)
else:
return (i, temp_name)
def convert_newlines_sep2tabs(fname, in_place=True, patt=r"\s+", tmp_dir=None, tmp_prefix="gxupload"):
"""
Combines above methods: convert_newlines() and sep2tabs()
so that files do not need to be read twice
Converts newlines in a file to posix newlines and replaces spaces with tabs.
>>> fname = get_test_fname('temp.txt')
>>> with open(fname, 'wt') as fh:
@@ -184,18 +162,7 @@ def convert_newlines_sep2tabs(fname, in_place=True, patt=r"\s+", tmp_dir=None, t
'1\\t2\\n3\\t4\\n'
"""
regexp = re.compile(patt)
fd, temp_name = tempfile.mkstemp(prefix=tmp_prefix, dir=tmp_dir)
with io.open(fd, mode="wt", encoding='utf-8') as fp:
for i, line in enumerate(io.open(fname, encoding='utf-8')):
line = line.rstrip('\r\n')
elems = regexp.split(line)
fp.write(u"%s\n" % '\t'.join(elems))
if in_place:
shutil.move(temp_name, fname)
# Return number of lines in file.
return (i + 1, None)
else:
return (i + 1, temp_name)
return convert_newlines(fname, in_place, tmp_dir, tmp_prefix, regexp=regexp)
def iter_headers(fname_or_file_prefix, sep, count=60, comment_designator=None):
@@ -474,6 +441,9 @@ def guess_ext(fname, sniff_order, is_binary=False):
>>> fname = get_test_fname('1.mtx')
>>> guess_ext(fname, sniff_order)
'mtx'
>>> fname = get_test_fname('1imzml')
>>> guess_ext(fname, sniff_order) # This test case is ensuring doesn't throw exception, actual value could change if non-utf encoding handling improves.
'data'
"""
file_prefix = FilePrefix(fname)
file_ext = run_sniffers_raw(file_prefix, sniff_order, is_binary)
@@ -555,8 +525,9 @@ def zip_single_fileobj(path):
class FilePrefix(object):
def __init__(self, filename):
binary = False
non_utf8_error = None
compressed_format = None
contents_header_bytes = None
contents_header = None # First MAX_BYTES of the file.
truncated = False
# A future direction to optimize sniffing even more for sniffers at the top of the list
@@ -565,20 +536,23 @@ class FilePrefix(object):
# populates contents_header while providing a StringIO-like interface until the file is read
# but then would fallback to native string_io()
try:
compressed_format, f = compression_utils.get_fileobj_raw(filename)
compressed_format, f = compression_utils.get_fileobj_raw(filename, "rb")
try:
contents_header = f.read(SNIFF_PREFIX_BYTES)
truncated = len(contents_header) == SNIFF_PREFIX_BYTES
contents_header_bytes = f.read(SNIFF_PREFIX_BYTES)
truncated = len(contents_header_bytes) == SNIFF_PREFIX_BYTES
contents_header = contents_header_bytes.decode("utf-8")
finally:
f.close()
except UnicodeDecodeError:
binary = True
except UnicodeDecodeError as e:
non_utf8_error = e
self.truncated = truncated
self.filename = filename
self.binary = binary
self.non_utf8_error = non_utf8_error
self.binary = non_utf8_error is not None # obviously wrong
self.compressed_format = compressed_format
self.contents_header = contents_header
self.contents_header_bytes = contents_header_bytes
self._file_size = None
@property
@@ -588,8 +562,8 @@ class FilePrefix(object):
return self._file_size
def string_io(self):
if self.binary:
raise Exception("Attempting to create a StringIO object for binary data.")
if self.non_utf8_error is not None:
raise self.non_utf8_error
rval = StringIO(self.contents_header)
return rval
+18 -2
View File
@@ -4,6 +4,7 @@ Tabular datatype
from __future__ import absolute_import
import abc
import binascii
import csv
import logging
import os
@@ -691,11 +692,11 @@ class BaseVcf(Tabular):
MetadataElement(name="viz_filter_cols", desc="Score column for visualization", default=[5], param=metadata.ColumnParameter, optional=True, multiple=True, visible=False)
MetadataElement(name="sample_names", default=[], desc="Sample names", readonly=True, visible=False, optional=True, no_value=[])
def sniff_prefix(self, file_prefix):
def _sniff(self, fname_or_file_prefix):
# Because this sniffer is run on compressed files that might be BGZF (due to the VcfGz subclass), we should
# handle unicode decode errors. This should ultimately be done in get_headers(), but guess_ext() currently
# relies on get_headers() raising this exception.
headers = get_headers(file_prefix, '\n', count=1)
headers = get_headers(fname_or_file_prefix, '\n', count=1)
return headers[0][0].startswith("##fileformat=VCF")
def display_peek(self, dataset):
@@ -745,14 +746,29 @@ class BaseVcf(Tabular):
class Vcf(BaseVcf):
file_ext = 'vcf'
def sniff_prefix(self, file_prefix):
return self._sniff(file_prefix)
class VcfGz(BaseVcf, binary.Binary):
# This class name is a misnomer, should be VcfBgzip
file_ext = 'vcf_bgzip'
compressed = True
compressed_format = "gzip"
MetadataElement(name="tabix_index", desc="Vcf Index File", param=metadata.FileParameter, file_ext="tbi", readonly=True, no_value=None, visible=False, optional=True)
def sniff(self, filename):
if not self._sniff(filename):
return False
# Check that the file is compressed with bgzip (not gzip), i.e. the
# compressed format is BGZF, as explained in
# http://samtools.github.io/hts-specs/SAMv1.pdf
with open(filename, 'rb') as fh:
fh.seek(-28, 2)
last28 = fh.read()
return binascii.hexlify(last28) == b'1f8b08040000000000ff0600424302001b0003000000000000000000'
def set_meta(self, dataset, **kwd):
super(BaseVcf, self).set_meta(dataset, **kwd)
""" Creates the index for the VCF file. """
+1
View File
@@ -0,0 +1 @@
0
+380
View File
@@ -0,0 +1,380 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<mzML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://psi.hupo.org/ms/mzml http://psidev.info/files/ms/mzML/xsd/mzML1.1.0.xsd" xmlns="http://psi.hupo.org/ms/mzml" version="1.1">
<cvList count="4">
<cv URI="http://ontologies.berkeleybop.org/pato.obo" fullName="Phenotype And Trait Ontology" id="PATO" version="releases/2017-07-10"/>
<cv URI="http://ontologies.berkeleybop.org/uo.obo" fullName="Units of Measurement Ontology" id="UO" version="releases/2017-09-25"/>
<cv URI="https://raw.githubusercontent.com/hupo-psi/psi-ms-cv/master/psi-ms.obo" fullName="Proteomics Standards Initiative Mass Spectrometry Ontology" id="MS" version="4.1.0"/>
<cv URI="https://raw.githubusercontent.com/imzML/imzML/master/imagingMS.obo" fullName="Mass Spectrometry Imaging Ontology" id="IMS" version="1.1.0"/>
</cvList>
<fileDescription>
<fileContent>
<cvParam cvRef="MS" accession="MS:1000579" name="MS1 spectrum"/>
<cvParam cvRef="MS" accession="MS:1000128" name="profile spectrum"/>
<cvParam cvRef="IMS" accession="IMS:1000080" name="universally unique identifier" value="9d501bdc53444916b7e97e795b02c856"/>
<cvParam cvRef="IMS" accession="IMS:1000091" name="ibd SHA-1" value="7e8fdb93053915d3edb51b70aa0619ac209964df"/>
<cvParam cvRef="IMS" accession="IMS:1000031" name="processed"/>
</fileContent>
<sourceFileList count="1">
<sourceFile id="sf1" location="C:\Users\Thorsten Schramm\Documents\Promotion\imzML\Website\files\Beispiel-Dateien\Example images\" name="Example.raw">
<cvParam cvRef="MS" accession="MS:1000563" name="Thermo RAW format"/>
<cvParam cvRef="MS" accession="MS:1000768" name="Thermo nativeID format"/>
<cvParam cvRef="MS" accession="MS:1000569" name="SHA-1" value="7623BE263B25FF99FDF017154B86FAB742D4BB0B"/>
</sourceFile>
</sourceFileList>
<contact>
<cvParam cvRef="MS" accession="MS:1000586" name="contact name" value="Thorsten Schramm"/>
<cvParam cvRef="MS" accession="MS:1000590" name="contact affiliation" value="Institut für Anorganische und Analytische Chemie"/>
<cvParam cvRef="MS" accession="MS:1000587" name="contact address" value="Schubertstraße 60, Haus 16, Gießen, Germany"/>
<cvParam cvRef="MS" accession="MS:1000589" name="contact email" value="thorsten.schramm@anorg.chemie.uni-.giessen.de"/>
</contact>
</fileDescription>
<referenceableParamGroupList count="4">
<referenceableParamGroup id="mzArray">
<cvParam cvRef="MS" accession="MS:1000576" name="no compression"/>
<cvParam cvRef="MS" accession="MS:1000514" name="m/z array" unitCvRef="MS" unitAccession="MS:1000040" unitName="m/z"/>
<cvParam cvRef="IMS" accession="IMS:1000101" name="external data" value="true"/>
<cvParam cvRef="MS" accession="MS:1000521" name="32-bit float"/>
</referenceableParamGroup>
<referenceableParamGroup id="intensityArray">
<cvParam cvRef="MS" accession="MS:1000576" name="no compression"/>
<cvParam cvRef="MS" accession="MS:1000515" name="intensity array" unitCvRef="MS" unitAccession="MS:1000131" unitName="number of detector counts"/>
<cvParam cvRef="IMS" accession="IMS:1000101" name="external data" value="true"/>
<cvParam cvRef="MS" accession="MS:1000521" name="32-bit float"/>
</referenceableParamGroup>
<referenceableParamGroup id="scan1">
<cvParam cvRef="MS" accession="MS:1000093" name="increasing m/z scan"/>
<cvParam cvRef="MS" accession="MS:1000512" name="filter string" value="ITMS - p NSI Full ms [100,00-800,00]"/>
</referenceableParamGroup>
<referenceableParamGroup id="spectrum1">
<cvParam cvRef="MS" accession="MS:1000579" name="MS1 spectrum"/>
<cvParam cvRef="MS" accession="MS:1000511" name="ms level" value="0"/>
<cvParam cvRef="MS" accession="MS:1000128" name="profile spectrum"/>
<cvParam cvRef="MS" accession="MS:1000129" name="negative scan"/>
</referenceableParamGroup>
</referenceableParamGroupList>
<sampleList count="1">
<sample id="sample1" name="Sample1">
<cvParam cvRef="MS" accession="MS:1000001" name="sample number" value="1"/>
</sample>
</sampleList>
<softwareList count="2">
<software id="Xcalibur" version="2.2">
<cvParam cvRef="MS" accession="MS:1000532" name="Xcalibur"/>
</software>
<software id="TMC" version="1.1 beta">
<cvParam cvRef="MS" accession="MS:1000799" name="custom unreleased software tool" value=""/>
</software>
</softwareList>
<scanSettingsList count="1">
<scanSettings id="scansettings1">
<cvParam cvRef="IMS" accession="IMS:1000401" name="top down"/>
<cvParam cvRef="IMS" accession="IMS:1000413" name="flyback"/>
<cvParam cvRef="IMS" accession="IMS:1000480" name="horizontal line scan"/>
<cvParam cvRef="IMS" accession="IMS:1000491" name="linescan left right"/>
<cvParam cvRef="IMS" accession="IMS:1000042" name="max count of pixels x" value="3"/>
<cvParam cvRef="IMS" accession="IMS:1000043" name="max count of pixels y" value="3"/>
<cvParam cvRef="IMS" accession="IMS:1000044" name="max dimension x" value="300" unitCvRef="UO" unitAccession="UO:0000017" unitName="micrometer"/>
<cvParam cvRef="IMS" accession="IMS:1000045" name="max dimension y" value="300" unitCvRef="UO" unitAccession="UO:0000017" unitName="micrometer"/>
<cvParam cvRef="IMS" accession="IMS:1000046" name="pixel size (x)" value="100.0" unitCvRef="UO" unitAccession="UO:0000017" unitName="micrometer"/>
<cvParam cvRef="IMS" accession="IMS:1000047" name="pixel size y" value="100.0" unitCvRef="UO" unitAccession="UO:0000017" unitName="micrometer"/>
</scanSettings>
</scanSettingsList>
<instrumentConfigurationList count="1">
<instrumentConfiguration id="LTQFTUltra0">
<cvParam cvRef="MS" accession="MS:1000557" name="LTQ FT Ultra"/>
<cvParam cvRef="MS" accession="MS:1000529" name="instrument serial number" value="none"/>
<componentList count="3">
<source order="1">
<cvParam cvRef="MS" accession="MS:1000073" name="electrospray ionization"/>
<cvParam cvRef="MS" accession="MS:1000485" name="nanospray inlet"/>
<cvParam cvRef="MS" accession="MS:1000844" name="focus diameter x" value="10.0"/>
<cvParam cvRef="MS" accession="MS:1000845" name="focus diameter y" value="10.0"/>
<cvParam cvRef="MS" accession="MS:1000846" name="pulse energy" value="10.0"/>
<cvParam cvRef="MS" accession="MS:1000847" name="pulse duration" value="10.0"/>
<cvParam cvRef="MS" accession="MS:1000848" name="attenuation" value="50.0"/>
<cvParam cvRef="MS" accession="MS:1000850" name="gas laser"/>
<cvParam cvRef="MS" accession="MS:1000836" name="dried droplet MALDI matrix preparation"/>
<cvParam cvRef="MS" accession="MS:1000835" name="matrix solution concentration" value="10.0"/>
<cvParam cvRef="MS" accession="MS:1000834" name="matrix solution" value="DHB"/>
</source>
<analyzer order="2">
<cvParam cvRef="MS" accession="MS:1000264" name="ion trap"/>
<cvParam cvRef="MS" accession="MS:1000014" name="accuracy" value="0.0" unitCvRef="MS" unitAccession="MS:1000040" unitName="m/z"/>
</analyzer>
<detector order="3">
<cvParam cvRef="MS" accession="MS:1000253" name="electron multiplier"/>
<cvParam cvRef="MS" accession="MS:1000120" name="transient recorder"/>
</detector>
</componentList>
<softwareRef ref="Xcalibur"/>
</instrumentConfiguration>
</instrumentConfigurationList>
<dataProcessingList count="2">
<dataProcessing id="XcaliburProcessing">
<processingMethod softwareRef="Xcalibur" order="1">
<cvParam cvRef="MS" accession="MS:1000594" name="low intensity data point removal"/>
</processingMethod>
</dataProcessing>
<dataProcessing id="TMCConversion">
<processingMethod softwareRef="TMC" order="2">
<cvParam cvRef="MS" accession="MS:1000544" name="Conversion to mzML"/>
</processingMethod>
</dataProcessing>
</dataProcessingList>
<run defaultInstrumentConfigurationRef="LTQFTUltra0" defaultSourceFileRef="sf1" id="Experiment01" sampleRef="sample1" startTimeStamp="2009-08-11T15:59:44">
<spectrumList count="9" defaultDataProcessingRef="XcaliburProcessing">
<spectrum id="Scan=1" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="0">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="121.85039039868471"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="1"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="1"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="16"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="33612"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=2" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="1">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="182.31835420101888"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="2"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="1"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="67208"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="100804"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=3" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="2">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="161.8091904482675"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="3"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="1"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="134400"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="167996"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=4" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="3">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="200.9633277092539"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="1"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="2"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="201592"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="235188"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=5" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="4">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="135.30584173158496"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="2"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="2"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="268784"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="302380"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=6" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="5">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="108.39597418421639"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="3"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="2"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="335976"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="369572"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=7" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="6">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="127.84664447846832"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="1"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="3"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="403168"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="436764"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=8" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="7">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="168.27018147522492"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="2"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="3"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="470360"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="503956"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=9" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="8">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="243.5395066031077"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="3"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="3"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="537552"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="571148"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
</spectrumList>
</run>
</mzML>
+380
View File
@@ -0,0 +1,380 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<mzML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://psi.hupo.org/ms/mzml http://psidev.info/files/ms/mzML/xsd/mzML1.1.0.xsd" xmlns="http://psi.hupo.org/ms/mzml" version="1.1">
<cvList count="4">
<cv URI="http://ontologies.berkeleybop.org/pato.obo" fullName="Phenotype And Trait Ontology" id="PATO" version="releases/2017-07-10"/>
<cv URI="http://ontologies.berkeleybop.org/uo.obo" fullName="Units of Measurement Ontology" id="UO" version="releases/2017-09-25"/>
<cv URI="https://raw.githubusercontent.com/hupo-psi/psi-ms-cv/master/psi-ms.obo" fullName="Proteomics Standards Initiative Mass Spectrometry Ontology" id="MS" version="4.1.0"/>
<cv URI="https://raw.githubusercontent.com/imzML/imzML/master/imagingMS.obo" fullName="Mass Spectrometry Imaging Ontology" id="IMS" version="1.1.0"/>
</cvList>
<fileDescription>
<fileContent>
<cvParam cvRef="MS" accession="MS:1000579" name="MS1 spectrum"/>
<cvParam cvRef="MS" accession="MS:1000128" name="profile spectrum"/>
<cvParam cvRef="IMS" accession="IMS:1000080" name="universally unique identifier" value="9d501bdc53444916b7e97e795b02c856"/>
<cvParam cvRef="IMS" accession="IMS:1000091" name="ibd SHA-1" value="7e8fdb93053915d3edb51b70aa0619ac209964df"/>
<cvParam cvRef="IMS" accession="IMS:1000031" name="processed"/>
</fileContent>
<sourceFileList count="1">
<sourceFile id="sf1" location="C:\Users\Thorsten Schramm\Documents\Promotion\imzML\Website\files\Beispiel-Dateien\Example images\" name="Example.raw">
<cvParam cvRef="MS" accession="MS:1000563" name="Thermo RAW format"/>
<cvParam cvRef="MS" accession="MS:1000768" name="Thermo nativeID format"/>
<cvParam cvRef="MS" accession="MS:1000569" name="SHA-1" value="7623BE263B25FF99FDF017154B86FAB742D4BB0B"/>
</sourceFile>
</sourceFileList>
<contact>
<cvParam cvRef="MS" accession="MS:1000586" name="contact name" value="Thorsten Schramm"/>
<cvParam cvRef="MS" accession="MS:1000590" name="contact affiliation" value="Institut für Anorganische und Analytische Chemie"/>
<cvParam cvRef="MS" accession="MS:1000587" name="contact address" value="Schubertstraße 60, Haus 16, Gießen, Germany"/>
<cvParam cvRef="MS" accession="MS:1000589" name="contact email" value="thorsten.schramm@anorg.chemie.uni-.giessen.de"/>
</contact>
</fileDescription>
<referenceableParamGroupList count="4">
<referenceableParamGroup id="mzArray">
<cvParam cvRef="MS" accession="MS:1000576" name="no compression"/>
<cvParam cvRef="MS" accession="MS:1000514" name="m/z array" unitCvRef="MS" unitAccession="MS:1000040" unitName="m/z"/>
<cvParam cvRef="IMS" accession="IMS:1000101" name="external data" value="true"/>
<cvParam cvRef="MS" accession="MS:1000521" name="32-bit float"/>
</referenceableParamGroup>
<referenceableParamGroup id="intensityArray">
<cvParam cvRef="MS" accession="MS:1000576" name="no compression"/>
<cvParam cvRef="MS" accession="MS:1000515" name="intensity array" unitCvRef="MS" unitAccession="MS:1000131" unitName="number of detector counts"/>
<cvParam cvRef="IMS" accession="IMS:1000101" name="external data" value="true"/>
<cvParam cvRef="MS" accession="MS:1000521" name="32-bit float"/>
</referenceableParamGroup>
<referenceableParamGroup id="scan1">
<cvParam cvRef="MS" accession="MS:1000093" name="increasing m/z scan"/>
<cvParam cvRef="MS" accession="MS:1000512" name="filter string" value="ITMS - p NSI Full ms [100,00-800,00]"/>
</referenceableParamGroup>
<referenceableParamGroup id="spectrum1">
<cvParam cvRef="MS" accession="MS:1000579" name="MS1 spectrum"/>
<cvParam cvRef="MS" accession="MS:1000511" name="ms level" value="0"/>
<cvParam cvRef="MS" accession="MS:1000128" name="profile spectrum"/>
<cvParam cvRef="MS" accession="MS:1000129" name="negative scan"/>
</referenceableParamGroup>
</referenceableParamGroupList>
<sampleList count="1">
<sample id="sample1" name="Sample1">
<cvParam cvRef="MS" accession="MS:1000001" name="sample number" value="1"/>
</sample>
</sampleList>
<softwareList count="2">
<software id="Xcalibur" version="2.2">
<cvParam cvRef="MS" accession="MS:1000532" name="Xcalibur"/>
</software>
<software id="TMC" version="1.1 beta">
<cvParam cvRef="MS" accession="MS:1000799" name="custom unreleased software tool" value=""/>
</software>
</softwareList>
<scanSettingsList count="1">
<scanSettings id="scansettings1">
<cvParam cvRef="IMS" accession="IMS:1000401" name="top down"/>
<cvParam cvRef="IMS" accession="IMS:1000413" name="flyback"/>
<cvParam cvRef="IMS" accession="IMS:1000480" name="horizontal line scan"/>
<cvParam cvRef="IMS" accession="IMS:1000491" name="linescan left right"/>
<cvParam cvRef="IMS" accession="IMS:1000042" name="max count of pixels x" value="3"/>
<cvParam cvRef="IMS" accession="IMS:1000043" name="max count of pixels y" value="3"/>
<cvParam cvRef="IMS" accession="IMS:1000044" name="max dimension x" value="300" unitCvRef="UO" unitAccession="UO:0000017" unitName="micrometer"/>
<cvParam cvRef="IMS" accession="IMS:1000045" name="max dimension y" value="300" unitCvRef="UO" unitAccession="UO:0000017" unitName="micrometer"/>
<cvParam cvRef="IMS" accession="IMS:1000046" name="pixel size (x)" value="100.0" unitCvRef="UO" unitAccession="UO:0000017" unitName="micrometer"/>
<cvParam cvRef="IMS" accession="IMS:1000047" name="pixel size y" value="100.0" unitCvRef="UO" unitAccession="UO:0000017" unitName="micrometer"/>
</scanSettings>
</scanSettingsList>
<instrumentConfigurationList count="1">
<instrumentConfiguration id="LTQFTUltra0">
<cvParam cvRef="MS" accession="MS:1000557" name="LTQ FT Ultra"/>
<cvParam cvRef="MS" accession="MS:1000529" name="instrument serial number" value="none"/>
<componentList count="3">
<source order="1">
<cvParam cvRef="MS" accession="MS:1000073" name="electrospray ionization"/>
<cvParam cvRef="MS" accession="MS:1000485" name="nanospray inlet"/>
<cvParam cvRef="MS" accession="MS:1000844" name="focus diameter x" value="10.0"/>
<cvParam cvRef="MS" accession="MS:1000845" name="focus diameter y" value="10.0"/>
<cvParam cvRef="MS" accession="MS:1000846" name="pulse energy" value="10.0"/>
<cvParam cvRef="MS" accession="MS:1000847" name="pulse duration" value="10.0"/>
<cvParam cvRef="MS" accession="MS:1000848" name="attenuation" value="50.0"/>
<cvParam cvRef="MS" accession="MS:1000850" name="gas laser"/>
<cvParam cvRef="MS" accession="MS:1000836" name="dried droplet MALDI matrix preparation"/>
<cvParam cvRef="MS" accession="MS:1000835" name="matrix solution concentration" value="10.0"/>
<cvParam cvRef="MS" accession="MS:1000834" name="matrix solution" value="DHB"/>
</source>
<analyzer order="2">
<cvParam cvRef="MS" accession="MS:1000264" name="ion trap"/>
<cvParam cvRef="MS" accession="MS:1000014" name="accuracy" value="0.0" unitCvRef="MS" unitAccession="MS:1000040" unitName="m/z"/>
</analyzer>
<detector order="3">
<cvParam cvRef="MS" accession="MS:1000253" name="electron multiplier"/>
<cvParam cvRef="MS" accession="MS:1000120" name="transient recorder"/>
</detector>
</componentList>
<softwareRef ref="Xcalibur"/>
</instrumentConfiguration>
</instrumentConfigurationList>
<dataProcessingList count="2">
<dataProcessing id="XcaliburProcessing">
<processingMethod softwareRef="Xcalibur" order="1">
<cvParam cvRef="MS" accession="MS:1000594" name="low intensity data point removal"/>
</processingMethod>
</dataProcessing>
<dataProcessing id="TMCConversion">
<processingMethod softwareRef="TMC" order="2">
<cvParam cvRef="MS" accession="MS:1000544" name="Conversion to mzML"/>
</processingMethod>
</dataProcessing>
</dataProcessingList>
<run defaultInstrumentConfigurationRef="LTQFTUltra0" defaultSourceFileRef="sf1" id="Experiment01" sampleRef="sample1" startTimeStamp="2009-08-11T15:59:44">
<spectrumList count="9" defaultDataProcessingRef="XcaliburProcessing">
<spectrum id="Scan=1" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="0">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="121.85039039868471"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="1"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="1"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="16"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="33612"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=2" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="1">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="182.31835420101888"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="2"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="1"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="67208"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="100804"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=3" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="2">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="161.8091904482675"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="3"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="1"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="134400"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="167996"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=4" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="3">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="200.9633277092539"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="1"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="2"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="201592"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="235188"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=5" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="4">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="135.30584173158496"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="2"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="2"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="268784"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="302380"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=6" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="5">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="108.39597418421639"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="3"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="2"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="335976"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="369572"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=7" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="6">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="127.84664447846832"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="1"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="3"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="403168"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="436764"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=8" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="7">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="168.27018147522492"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="2"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="3"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="470360"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="503956"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<spectrum id="Scan=9" defaultArrayLength="0" dataProcessingRef="XcaliburProcessing" sourceFileRef="sf1" index="8">
<referenceableParamGroupRef ref="spectrum1"/>
<cvParam cvRef="MS" accession="MS:1000285" name="total ion current" value="243.5395066031077"/>
<scanList count="1">
<cvParam cvRef="MS" accession="MS:1000795" name="no combination"/>
<scan instrumentConfigurationRef="LTQFTUltra0">
<referenceableParamGroupRef ref="scan1"/>
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="3"/>
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="3"/>
</scan>
</scanList>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="mzArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="537552"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
<binaryDataArray encodedLength="0">
<referenceableParamGroupRef ref="intensityArray"/>
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="8399"/>
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="571148"/>
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="33596"/>
<binary />
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
</spectrumList>
</run>
</mzML>
Binary file not shown.
Binary file not shown.
+40 -14
View File
@@ -34,20 +34,46 @@ class ConditionalDependencies(object):
def parse_configs(self):
self.config = load_app_properties(config_file=self.config_file)
job_conf_xml = self.config.get(
"job_config_file",
join(dirname(self.config_file), 'job_conf.xml'))
try:
for plugin in ElementTree.parse(job_conf_xml).find('plugins').findall('plugin'):
if 'load' in plugin.attrib:
self.job_runners.append(plugin.attrib['load'])
except (OSError, IOError):
pass
try:
for plugin in ElementTree.parse(job_conf_xml).findall('.//destination/param[@id="rules_module"]'):
self.job_rule_modules.append(plugin.text)
except (OSError, IOError):
pass
def load_job_config_dict(job_conf_dict):
for runner in job_conf_dict.get("runners"):
if "load" in runner:
self.job_runners.append(runner.get("load"))
if "rules_module" in runner:
self.job_rule_modules.append(plugin.text)
if "params" in runner:
runner_params = runner["params"]
if "rules_module" in runner_params:
self.job_rule_modules.append(plugin.text)
if "job_config" in self.config:
load_job_config_dict(self.config.get("job_config"))
else:
job_conf_path = self.config.get(
"job_config_file",
join(dirname(self.config_file), 'job_conf.xml'))
if '.xml' in job_conf_path:
try:
try:
for plugin in ElementTree.parse(job_conf_path).find('plugins').findall('plugin'):
if 'load' in plugin.attrib:
self.job_runners.append(plugin.attrib['load'])
except (OSError, IOError):
pass
try:
for plugin in ElementTree.parse(job_conf_path).findall('.//destination/param[@id="rules_module"]'):
self.job_rule_modules.append(plugin.text)
except (OSError, IOError):
pass
except ElementTree.ParseError:
pass
else:
try:
job_conf_dict = yaml.safe_load(job_conf_path)
load_job_config_dict(job_conf_dict)
except (OSError, IOError):
pass
object_store_conf_xml = self.config.get(
"object_store_config_file",
join(dirname(self.config_file), 'object_store_conf.xml'))
@@ -36,7 +36,7 @@ cachetools==3.1.0
certifi==2019.3.9
cffi==1.12.3
chardet==3.0.4
cheetah3==3.2.1
cheetah3==3.2.2
cliff==2.14.1
cloudauthz==0.2.0
cloudbridge==2.0.0
+296 -128
View File
@@ -20,6 +20,7 @@ from tempfile import NamedTemporaryFile
from xml.etree import ElementTree
import six
import yaml
from pulsar.client.staging import COMMAND_VERSION_FILENAME
import galaxy
@@ -46,7 +47,7 @@ log = logging.getLogger(__name__)
# that import Galaxy internals - but it shouldn't be used in Galaxy's code
# itself.
TOOL_PROVIDED_JOB_METADATA_FILE = 'galaxy.json'
TOOL_PROVIDED_JOB_METADATA_KEYS = ['name', 'info', 'dbkey']
TOOL_PROVIDED_JOB_METADATA_KEYS = ['name', 'info', 'dbkey', 'created_from_basename']
# Override with config.default_job_shell.
DEFAULT_JOB_SHELL = '/bin/bash'
@@ -80,10 +81,6 @@ class JobDestination(Bunch):
super(JobDestination, self).__init__(**kwds)
# Store tags as a list
if self.tags is not None:
self['tags'] = [x.strip() for x in self.tags.split(',')]
class JobToolConfiguration(Bunch):
"""
@@ -112,6 +109,139 @@ def config_exception(e, file):
return Exception(message)
def job_config_xml_to_dict(config, root):
config_dict = {}
runners = {}
config_dict["runners"] = runners
# Parser plugins section populate 'runners' and 'dynamic' in config_dict.
plugins = root.find('plugins')
if plugins is not None:
for plugin in ConfiguresHandlers._findall_with_required(plugins, 'plugin', ('id', 'type', 'load')):
if plugin.get('type') == 'runner':
workers = plugin.get('workers', plugins.get('workers', JobConfiguration.DEFAULT_NWORKERS))
runner_kwds = JobConfiguration.get_params(config, plugin)
plugin_id = plugin.get('id')
runner_info = dict(id=plugin_id,
load=plugin.get('load'),
workers=int(workers),
kwds=runner_kwds)
runners[plugin_id] = runner_info
else:
log.error('Unknown plugin type: %s' % plugin.get('type'))
for plugin in ConfiguresHandlers._findall_with_required(plugins, 'plugin', ('id', 'type')):
if plugin.get('id') == 'dynamic' and plugin.get('type') == 'runner':
config_dict["dynamic"] = JobConfiguration.get_params(config, plugin)
handling_config_dict = ConfiguresHandlers.xml_to_dict(config, root.find("handlers"))
config_dict["handling"] = handling_config_dict
# Parse destinations
environments = []
destinations = root.find('destinations')
for destination in ConfiguresHandlers._findall_with_required(destinations, 'destination', ('id', 'runner')):
destination_id = destination.get('id')
destination_metrics = destination.get("metrics", None)
environment = {"id": destination_id}
metrics_to_dict = {"src": "default"}
if destination_metrics:
if not util.asbool(destination_metrics):
metrics_to_dict = {"src": "disabled"}
else:
metrics_to_dict = {"src": "path", "path": destination_metrics}
else:
metrics_elements = ConfiguresHandlers._findall_with_required(destination, 'job_metrics', ())
if metrics_elements:
metrics_to_dict = {"src": "xml_element", 'xml_element': metrics_elements[0]}
environment["metrics"] = metrics_to_dict
params = JobConfiguration.get_params(config, destination)
# TODO: handle enabled/disabled in configure_from
environment['params'] = params
environment['env'] = JobConfiguration.get_envs(destination)
destination_resubmits = JobConfiguration.get_resubmits(destination)
if destination_resubmits:
environment['resubmit'] = destination_resubmits
# TODO: handle empty resubmits defaults in configure_from
runner = destination.get('runner')
if runner:
environment['runner'] = runner
tags = destination.get('tags')
# Store tags as a list
if tags is not None:
tags = [x.strip() for x in tags.split(',')]
environment['tags'] = tags
environments.append(environment)
config_dict['execution'] = {
'environments': environments,
}
default_destination = ConfiguresHandlers.get_xml_default(config, destinations)
if default_destination:
config_dict['execution']['default'] = default_destination
resources_config_dict = {}
resource_groups = {}
# Parse resources...
resources = root.find('resources')
if resources is not None:
default_resource_group = resources.get("default", None)
if default_resource_group:
resources_config_dict["default"] = default_resource_group
for group in ConfiguresHandlers._findall_with_required(resources, 'group'):
group_id = group.get('id')
fields_str = group.get('fields', None) or group.text or ''
fields = [f for f in fields_str.split(",") if f]
resource_groups[group_id] = fields
resources_config_dict["groups"] = resource_groups
config_dict["resources"] = resources_config_dict
# Parse tool mappings
tools = root.find('tools')
config_dict['tools'] = []
if tools is not None:
for tool in ConfiguresHandlers._findall_with_required(tools, 'tool'):
# There can be multiple definitions with identical ids, but different params
tool_mapping_conf = {}
for key in ['handler', 'destination', 'id', 'resources']:
value = tool.get(key)
if value:
if key == "destination":
key = "environment"
tool_mapping_conf[key] = value
tool_mapping_conf["params"] = JobConfiguration.get_params(config, tool)
config_dict['tools'].append(tool_mapping_conf)
limits_config = []
limits = root.find('limits')
if limits is not None:
for limit in JobConfiguration._findall_with_required(limits, 'limit', ('type',)):
limit_dict = {}
for key in ['type', 'tag', 'id', 'window']:
if key == 'type' and key.startswith('destination_'):
key = 'environment_%s' % key[len("destination_"):]
value = limit.get(key)
if value:
limit_dict[key] = value
limit_dict['value'] = limit.text
limits_config.append(limit_dict)
config_dict['limits'] = limits_config
return config_dict
class JobConfiguration(ConfiguresHandlers):
"""A parser and interface to advanced job management features.
@@ -162,7 +292,7 @@ class JobConfiguration(ConfiguresHandlers):
default_resubmit_condition = self.app.config.default_job_resubmission_condition
if default_resubmit_condition:
default_resubmits.append(dict(
destination=None,
environment=None,
condition=default_resubmit_condition,
handler=None,
delay=None,
@@ -171,10 +301,26 @@ class JobConfiguration(ConfiguresHandlers):
self.__parse_resource_parameters()
# Initialize the config
job_config_file = self.app.config.job_config_file
try:
tree = load(job_config_file)
self.__parse_job_conf_xml(tree)
if 'job_config' in self.app.config.config_dict:
job_config_dict = self.app.config.config_dict["job_config"]
else:
job_config_file = self.app.config.job_config_file
if '.xml' in job_config_file:
tree = load(job_config_file)
job_config_dict = self.__parse_job_conf_xml(tree)
else:
with open(job_config_file, "r") as f:
job_config_dict = yaml.safe_load(f)
# Load tasks if configured
if self.app.config.use_tasked_jobs:
job_config_dict["runners"]["tasks"] = dict(id='tasks', load='tasks', workers=self.app.config.local_task_queue_workers, kwds={})
self._configure_from_dict(job_config_dict)
log.debug('Done loading job configuration')
except IOError:
log.warning('Job configuration "%s" does not exist, using default job configuration',
self.app.config.job_config_file)
@@ -182,43 +328,33 @@ class JobConfiguration(ConfiguresHandlers):
except Exception as e:
raise config_exception(e, job_config_file)
def __parse_job_conf_xml(self, tree):
"""Loads the new-style job configuration from options in the job config file (by default, job_conf.xml).
:param tree: Object representing the root ``<job_conf>`` object in the job config file.
:type tree: ``xml.etree.ElementTree.Element``
"""
root = tree.getroot()
log.debug('Loading job configuration from %s' % self.app.config.job_config_file)
# Parse job plugins
plugins = root.find('plugins')
if plugins is not None:
for plugin in self._findall_with_required(plugins, 'plugin', ('id', 'type', 'load')):
if plugin.get('type') == 'runner':
workers = plugin.get('workers', plugins.get('workers', JobConfiguration.DEFAULT_NWORKERS))
runner_kwds = self.__get_params(plugin)
if not self.__is_enabled(runner_kwds):
def _configure_from_dict(self, job_config_dict):
for runner_id, runner_info in job_config_dict["runners"].items():
if "kwds" not in runner_info:
# convert all 'extra' parameters into kwds, allows defining a runner
# with a flat dictionary.
kwds = {}
for key, value in runner_info.items():
if key in ['id', 'load', 'workers']:
continue
runner_info = dict(id=plugin.get('id'),
load=plugin.get('load'),
workers=int(workers),
kwds=runner_kwds)
self.runner_plugins.append(runner_info)
else:
log.error('Unknown plugin type: %s' % plugin.get('type'))
for plugin in self._findall_with_required(plugins, 'plugin', ('id', 'type')):
if plugin.get('id') == 'dynamic' and plugin.get('type') == 'runner':
self.dynamic_params = self.__get_params(plugin)
kwds[key] = value
runner_info["kwds"] = kwds
# Load tasks if configured
if self.app.config.use_tasked_jobs:
self.runner_plugins.append(dict(id='tasks', load='tasks', workers=self.app.config.local_task_queue_workers))
if not self.__is_enabled(runner_info.get("kwds")):
continue
runner_info["id"] = runner_id
if runner_id == "dynamic":
log.warning('Deprecated treatment of dynamic running configuration as an actual job runner.')
self.dynamic_params = runner_info["kwds"]
continue
self.runner_plugins.append(runner_info)
if "dynamic" in job_config_dict:
self.dynamic_params = job_config_dict.get("dynamic", None)
# Parse handlers
handlers_conf = root.find('handlers')
self._init_handler_assignment_methods(handlers_conf)
self._init_handlers(handlers_conf)
handling_config_dict = job_config_dict.get("handling", {})
self._init_handler_assignment_methods(handling_config_dict)
self._init_handlers(handling_config_dict)
if not self.handler_assignment_methods_configured:
self._set_default_handler_assignment_methods()
else:
@@ -227,38 +363,51 @@ class JobConfiguration(ConfiguresHandlers):
for tag, handlers in [(t, h) for t, h in self.handlers.items() if isinstance(h, list)]:
log.info("Tag [%s] handlers: %s", tag, ', '.join(handlers))
# Parse destinations
destinations = root.find('destinations')
# Parse environments
job_metrics = self.app.job_metrics
for destination in self._findall_with_required(destinations, 'destination', ('id', 'runner')):
id = destination.get('id')
destination_metrics = destination.get("metrics", None)
if destination_metrics:
if not util.asbool(destination_metrics):
# disable
job_metrics.set_destination_instrumenter(id, None)
else:
metrics_conf_path = self.app.config.resolve_path(destination_metrics)
job_metrics.set_destination_conf_file(id, metrics_conf_path)
else:
metrics_elements = self._findall_with_required(destination, 'job_metrics', ())
if metrics_elements:
job_metrics.set_destination_conf_element(id, metrics_elements[0])
job_destination = JobDestination(**dict(destination.items()))
params = self.__get_params(destination)
if not self.__is_enabled(params):
execution_dict = job_config_dict.get('execution', {})
environments = execution_dict.get("environments", [])
enviroment_iter = map(lambda e: (e["id"], e), environments) if isinstance(environments, list) else environments.items()
for environment_id, environment_dict in enviroment_iter:
metrics = environment_dict.get("metrics") or {"src": "default"}
metrics_src = metrics.get("src") or "default"
if metrics_src != "default":
# customized metrics for this environment.
if metrics_src == "disabled":
job_metrics.set_destination_instrumenter(environment_id, None)
elif metrics_src == "xml_element":
metrics_element = metrics.get("xml_element")
job_metrics.set_destination_conf_element(environment_id, metrics_element)
elif metrics_src == "path":
metrics_conf_path = self.app.config.resolve_path(metrics.get("path"))
job_metrics.set_destination_conf_file(environment_id, metrics_conf_path)
destination_kwds = {}
params = environment_dict.get("params")
if params is None:
# Treat the excess keys in the environment as the destination parameters
# allowing a flat configuration of these things.
params = {}
for key, value in environment_dict.items():
if key in ['id', 'tags', 'runner', 'shell', 'env', 'resubmit']:
continue
params[key] = value
environment_dict["params"] = params
for key in ['tags', 'runner', 'shell', 'env', 'resubmit', 'params']:
if key in environment_dict:
destination_kwds[key] = environment_dict[key]
destination_kwds["id"] = environment_id
job_destination = JobDestination(**destination_kwds)
if not self.__is_enabled(job_destination.params):
continue
job_destination['params'] = params
job_destination['env'] = self.__get_envs(destination)
destination_resubmits = self.__get_resubmits(destination)
if destination_resubmits:
resubmits = destination_resubmits
else:
if not job_destination.resubmit:
resubmits = self.default_resubmits
job_destination["resubmit"] = resubmits
job_destination.resubmit = resubmits
self.destinations[id] = (job_destination,)
self.destinations[environment_id] = (job_destination,)
if job_destination.tags is not None:
for tag in job_destination.tags:
if tag not in self.destinations:
@@ -266,29 +415,30 @@ class JobConfiguration(ConfiguresHandlers):
self.destinations[tag].append(job_destination)
# Determine the default destination
self.default_destination_id = self._get_default(
self.app.config, destinations, list(self.destinations.keys()), auto=True)
self.default_destination_id = self._ensure_default_set(execution_dict.get("default"), list(self.destinations.keys()), auto=True)
# Parse resources...
resources = root.find('resources')
if resources is not None:
self.default_resource_group = resources.get("default", None)
for group in self._findall_with_required(resources, 'group'):
id = group.get('id')
fields_str = group.get('fields', None) or group.text or ''
fields = [f for f in fields_str.split(",") if f]
self.resource_groups[id] = fields
# Read in resources
resources = job_config_dict.get("resources", {})
self.default_resource_group = resources.get("default", None)
for group_id, fields in resources.get("groups", {}).items():
self.resource_groups[group_id] = fields
# Parse tool mappings
tools = root.find('tools')
if tools is not None:
for tool in self._findall_with_required(tools, 'tool'):
# There can be multiple definitions with identical ids, but different params
id = tool.get('id').lower().rstrip('/')
if id not in self.tools:
self.tools[id] = list()
self.tools[id].append(JobToolConfiguration(**dict(tool.items())))
self.tools[id][-1]['params'] = self.__get_params(tool)
tools = job_config_dict.get('tools', [])
for tool in tools:
tool_id = tool.get('id').lower().rstrip('/')
if tool_id not in self.tools:
self.tools[tool_id] = list()
params = tool.get("params")
if params is None:
params = {}
for key, value in tool.items():
if key in ["environment", "handler", "id"]:
continue
params[key] = value
tool["params"] = params
if "environment" in tool:
tool["destination"] = tool.pop("environment")
self.tools[tool_id].append(JobToolConfiguration(**dict(tool.items())))
types = dict(registered_user_concurrent_jobs=int,
anonymous_user_concurrent_jobs=int,
@@ -297,26 +447,28 @@ class JobConfiguration(ConfiguresHandlers):
output_size=util.size_to_bytes)
# Parse job limits
limits = root.find('limits')
if limits is not None:
for limit in self._findall_with_required(limits, 'limit', ('type',)):
type = limit.get('type')
# concurrent_jobs renamed to destination_user_concurrent_jobs in job_conf.xml
if type in ('destination_user_concurrent_jobs', 'concurrent_jobs', 'destination_total_concurrent_jobs'):
id = limit.get('tag', None) or limit.get('id')
if type == 'destination_total_concurrent_jobs':
self.limits.destination_total_concurrent_jobs[id] = int(limit.text)
else:
self.limits.destination_user_concurrent_jobs[id] = int(limit.text)
elif type == 'total_walltime':
self.limits.total_walltime["window"] = (
int(limit.get('window')) or 30
)
self.limits.total_walltime["raw"] = (
types.get(type, str)(limit.text)
)
elif limit.text:
self.limits.__dict__[type] = types.get(type, str)(limit.text)
for limit_dict in job_config_dict.get("limits", []):
limit_type = limit_dict.get('type')
if limit_type.startswith("environment_"):
limit_type = 'destination_%s' % limit_type[len("environment_"):]
limit_value = limit_dict.get("value")
# concurrent_jobs renamed to destination_user_concurrent_jobs in job_conf.xml
if limit_type in ('destination_user_concurrent_jobs', 'concurrent_jobs', 'destination_total_concurrent_jobs'):
id = limit_dict.get('tag', None) or limit_dict.get('id')
if limit_type == 'destination_total_concurrent_jobs':
self.limits.destination_total_concurrent_jobs[id] = int(limit_value)
else:
self.limits.destination_user_concurrent_jobs[id] = int(limit_value)
elif limit_type == 'total_walltime':
self.limits.total_walltime["window"] = (
int(limit_dict.get('window')) or 30
)
self.limits.total_walltime["raw"] = (
types.get(limit_type, str)(limit_value)
)
elif limit_value:
self.limits.__dict__[limit_type] = types.get(limit_type, str)(limit_value)
if self.limits.walltime is not None:
h, m, s = [int(v) for v in self.limits.walltime.split(':')]
@@ -329,13 +481,23 @@ class JobConfiguration(ConfiguresHandlers):
0, s, 0, 0, m, h
)
log.debug('Done loading job configuration')
def __parse_job_conf_xml(self, tree):
"""Loads the new-style job configuration from options in the job config file (by default, job_conf.xml).
def _parse_handler(self, handler_id, handler_element):
for plugin in handler_element.findall('plugin'):
:param tree: Object representing the root ``<job_conf>`` object in the job config file.
:type tree: ``xml.etree.ElementTree.Element``
"""
root = tree.getroot()
log.debug('Loading job configuration from %s' % self.app.config.job_config_file)
job_config_dict = job_config_xml_to_dict(self.app.config, root)
return job_config_dict
def _parse_handler(self, handler_id, process_dict):
for plugin_id in process_dict.get("plugins") or []:
if handler_id not in self.handler_runner_plugins:
self.handler_runner_plugins[handler_id] = []
self.handler_runner_plugins[handler_id].append(plugin.get('id'))
self.handler_runner_plugins[handler_id].append(plugin_id)
def __set_default_job_conf(self):
# Run jobs locally
@@ -383,14 +545,8 @@ class JobConfiguration(ConfiguresHandlers):
def __parse_resource_parameters(self):
self.resource_parameters = util.parse_resource_parameters(self.app.config.job_resource_params_file)
def __get_params(self, parent):
"""Parses any child <param> tags in to a dictionary suitable for persistence.
:param parent: Parent element in which to find child <param> tags.
:type parent: ``xml.etree.ElementTree.Element``
:returns: dict
"""
@staticmethod
def get_params(config, parent):
rval = {}
for param in parent.findall('param'):
key = param.get('id')
@@ -406,12 +562,23 @@ class JobConfiguration(ConfiguresHandlers):
param_value = os.environ.get(environ_var, param_value)
elif 'from_config' in param.attrib:
config_val = param.attrib['from_config']
param_value = self.app.config.config_dict.get(config_val, param_value)
param_value = config.config_dict.get(config_val, param_value)
rval[key] = param_value
return rval
def __get_envs(self, parent):
def __get_params(self, parent):
"""Parses any child <param> tags in to a dictionary suitable for persistence.
:param parent: Parent element in which to find child <param> tags.
:type parent: ``xml.etree.ElementTree.Element``
:returns: dict
"""
return JobConfiguration.get_params(self.app.config, parent)
@staticmethod
def get_envs(parent):
"""Parses any child <env> tags in to a dictionary suitable for persistence.
:param parent: Parent element in which to find child <env> tags.
@@ -430,7 +597,8 @@ class JobConfiguration(ConfiguresHandlers):
))
return rval
def __get_resubmits(self, parent):
@staticmethod
def get_resubmits(parent):
"""Parses any child <resubmit> tags in to a dictionary suitable for persistence.
:param parent: Parent element in which to find child <resubmit> tags.
@@ -442,7 +610,7 @@ class JobConfiguration(ConfiguresHandlers):
for resubmit in parent.findall('resubmit'):
rval.append(dict(
condition=resubmit.get('condition'),
destination=resubmit.get('destination'),
environment=resubmit.get('destination'),
handler=resubmit.get('handler'),
delay=resubmit.get('delay'),
))
@@ -451,7 +619,7 @@ class JobConfiguration(ConfiguresHandlers):
def __is_enabled(self, params):
"""Check for an enabled parameter - pop it out - and return as boolean."""
enabled = True
if "enabled" in params:
if "enabled" in (params or {}):
raw_enabled = params.pop("enabled")
enabled = util.asbool(raw_enabled)
@@ -596,7 +764,7 @@ class JobConfiguration(ConfiguresHandlers):
log.warning("Job runner classes must be subclassed from BaseJobRunner, %s has bases: %s" % (id, runner_class.__bases__))
continue
try:
rval[id] = runner_class(self.app, runner['workers'], **runner.get('kwds', {}))
rval[id] = runner_class(self.app, runner.get('workers', JobConfiguration.DEFAULT_NWORKERS), **runner.get('kwds', {}))
except TypeError:
log.exception("Job runner '%s:%s' has not been converted to a new-style runner or encountered TypeError on load",
module_name, class_name)
+3 -4
View File
@@ -146,10 +146,9 @@ def check_output(stdio_regexes, stdio_exit_codes, stdout, stderr, tool_exit_code
if stderr:
state = DETECTED_JOB_STATE.GENERIC_ERROR
if DETECTED_JOB_STATE != DETECTED_JOB_STATE.OK and stderr:
if stderr:
peak = stderr[0:ERROR_PEAK]
log.debug("job failed, standard error is - [%s]" % peak)
if state != DETECTED_JOB_STATE.OK:
peak = stderr[0:ERROR_PEAK] if stderr else ""
log.debug("job failed, detected state %s, standard error is - [%s]" % (state, peak))
except Exception:
log.exception("Job state check encountered unexpected exception; assuming execution successful")
+2 -1
View File
@@ -12,6 +12,7 @@ from time import sleep
import packaging.version
import pulsar.core
import six
import yaml
from pulsar.client import (
build_client_manager,
@@ -449,7 +450,7 @@ class PulsarJobRunner(AsynchronousJobRunner):
user = job_wrapper.get_job().user
if user:
for key, value in params.items():
if value:
if value and isinstance(value, six.string_types):
params[key] = model.User.expand_user_properties(user, value)
env = getattr(job_wrapper.job_destination, "env", [])
+2 -6
View File
@@ -28,13 +28,9 @@ SLURM_MEMORY_LIMIT_EXCEEDED_MSG = 'slurmstepd: error: Exceeded job memory limit'
SLURM_MEMORY_LIMIT_EXCEEDED_PARTIAL_WARNINGS = [': Exceeded job memory limit at some point.',
': Exceeded step memory limit at some point.']
SLURM_MEMORY_LIMIT_SCAN_SIZE = 16 * 1024 * 1024 # 16MB
SLURM_UNABLE_TO_ADD_TASK_TO_MEMORY_CG_MSG_RE = re.compile(r"""slurmstepd: error: task/cgroup: unable to add task\[pid=\d+\] to memory cg '\(null\)'$""")
SLURM_UNABLE_TO_CREATE_CGROUP_MSG_RE = re.compile(r"""slurmstepd: error: xcgroup_instantiate: unable to create cgroup '[^']+' : No space left on device$""")
SLURM_UNABLE_TO_INSTANCIATE_CGROUP_MSG_RE = re.compile(r"""slurmstepd: error: jobacct_gather/cgroup: unable to instanciate (job|user) \d+ memory cgroup$""")
SLURM_CGROUP_RE = re.compile(r"""slurmstepd: .*cgroup.*$""")
SLURM_TOP_WARNING_RES = (
SLURM_UNABLE_TO_ADD_TASK_TO_MEMORY_CG_MSG_RE,
SLURM_UNABLE_TO_CREATE_CGROUP_MSG_RE,
SLURM_UNABLE_TO_INSTANCIATE_CGROUP_MSG_RE
SLURM_CGROUP_RE,
)
# These messages are returned to the user
@@ -97,7 +97,8 @@ def _handle_resubmit_definitions(resubmit_definitions, app, job_runner, job_stat
else:
job_log_prefix = "(%s)" % (job_state.job_wrapper.job_id)
destination = resubmit['destination']
# Is destination needed here, might these be serialized to the database?
destination = resubmit.get('environment') or resubmit.get('destination')
log.info("%s Job will be resubmitted to '%s' because %s at "
"the '%s' destination",
job_log_prefix,
+9 -19
View File
@@ -1,6 +1,5 @@
import functools
import logging
import os
import requests
from beaker.cache import CacheManager
@@ -26,8 +25,8 @@ class CitationsManager(object):
citation_collection.add(citation)
return citation_collection.citations
def parse_citation(self, citation_elem, tool_directory):
return parse_citation(citation_elem, tool_directory, self)
def parse_citation(self, citation_elem):
return parse_citation(citation_elem, self)
def _get_tool(self, tool_id):
tool = self.app.toolbox.get_tool(tool_id)
@@ -55,10 +54,9 @@ class DoiCache(object):
return self._cache.get(key=doi, createfunc=createfunc)
def parse_citation(elem, directory, citation_manager):
""" Parse an abstract citation entry from the specified XML element.
The directory parameter should be used to find external files for this
citation.
def parse_citation(elem, citation_manager):
"""
Parse an abstract citation entry from the specified XML element.
"""
citation_type = elem.attrib.get('type', None)
citation_class = CITATION_CLASSES.get(citation_type, None)
@@ -66,7 +64,7 @@ def parse_citation(elem, directory, citation_manager):
log.warning("Unknown or unspecified citation type: %s" % citation_type)
return None
try:
citation = citation_class(elem, directory, citation_manager)
citation = citation_class(elem, citation_manager)
except Exception as e:
raise Exception("Invalid citation of type '%s' with content '%s': %s" % (citation_type, elem.text, e))
return citation
@@ -118,16 +116,8 @@ class BaseCitation(object):
class BibtexCitation(BaseCitation):
def __init__(self, elem, directory, citation_manager):
bibtex_file = elem.attrib.get("file", None)
if bibtex_file:
raw_bibtex = open(os.path.join(directory, bibtex_file), "r").read()
else:
raw_bibtex = elem.text.strip()
self._set_raw_bibtex(raw_bibtex)
def _set_raw_bibtex(self, raw_bibtex):
self.raw_bibtex = raw_bibtex
def __init__(self, elem, citation_manager):
self.raw_bibtex = elem.text.strip()
def to_bibtex(self):
return self.raw_bibtex
@@ -136,7 +126,7 @@ class BibtexCitation(BaseCitation):
class DoiCitation(BaseCitation):
BIBTEX_UNSET = object()
def __init__(self, elem, directory, citation_manager):
def __init__(self, elem, citation_manager):
self.__doi = elem.text.strip()
self.doi_cache = citation_manager.doi_cache
self.raw_bibtex = DoiCitation.BIBTEX_UNSET
+4 -2
View File
@@ -300,7 +300,8 @@ class HDASerializer( # datasets._UnflattenedMetadataDatasetAssociationSerialize
'annotation',
'api_type'
'api_type',
'created_from_basename',
], include_keys_from='summary')
self.add_view('extended', [
@@ -354,7 +355,8 @@ class HDASerializer( # datasets._UnflattenedMetadataDatasetAssociationSerialize
# TODO: to DatasetAssociationSerializer
'accessible' : lambda i, k, user=None, **c: self.manager.is_accessible(i, user, **c),
'api_type' : lambda *a, **c: 'file',
'type' : lambda *a, **c: 'file'
'type' : lambda *a, **c: 'file',
'created_from_basename' : lambda i, k, **c: i.created_from_basename,
})
def serialize(self, hda, keys, user=None, **context):
+1 -1
View File
@@ -143,7 +143,7 @@ class PortableDirectoryMetadataGenerator(MetadataCollectionStrategy):
script_path = os.path.join(metadata_dir, "set.py")
with open(script_path, "w") as f:
f.write(SET_METADATA_SCRIPT)
return 'pwd; python "metadata/set.py"'
return 'python "metadata/set.py"'
else:
# return args to galaxy_ext.metadata.set_metadata required to build
return ''
+29 -7
View File
@@ -42,6 +42,7 @@ from sqlalchemy.orm import (
)
from sqlalchemy.schema import UniqueConstraint
import galaxy.exceptions
import galaxy.model.metadata
import galaxy.model.orm.now
import galaxy.model.tags
@@ -2156,8 +2157,10 @@ class Dataset(StorableObject, RepresentById):
def get_file_name(self):
if not self.external_filename:
assert self.object_store is not None, "Object Store has not been initialized for dataset %s" % self.id
filename = self.object_store.get_filename(self)
return filename
if self.object_store.exists(self):
return self.object_store.get_filename(self)
else:
return ''
else:
filename = self.external_filename
# Make filename absolute
@@ -2175,10 +2178,16 @@ class Dataset(StorableObject, RepresentById):
# actual database column so if SA instantiates this object - the
# attribute won't exist yet.
if not getattr(self, "external_extra_files_path", None):
return self.object_store.get_filename(self, dir_only=True, extra_dir=self._extra_files_rel_path)
if self.object_store.exists(self, dir_only=True, extra_dir=self._extra_files_rel_path):
return self.object_store.get_filename(self, dir_only=True, extra_dir=self._extra_files_rel_path)
return ''
else:
return os.path.abspath(self.external_extra_files_path)
def create_extra_files_path(self):
if not self.extra_files_path_exists():
self.object_store.create(self, dir_only=True, extra_dir=self._extra_files_rel_path)
def set_extra_files_path(self, extra_files_path):
if not extra_files_path:
self.external_extra_files_path = None
@@ -2266,11 +2275,12 @@ class Dataset(StorableObject, RepresentById):
def full_delete(self):
"""Remove the file and extra files, marks deleted and purged"""
# os.unlink( self.file_name )
self.object_store.delete(self)
try:
self.object_store.delete(self)
except galaxy.exceptions.ObjectNotFound:
pass
if self.object_store.exists(self, extra_dir=self._extra_files_rel_path, dir_only=True):
self.object_store.delete(self, entire_dir=True, extra_dir=self._extra_files_rel_path, dir_only=True)
# if os.path.exists( self.extra_files_path ):
# shutil.rmtree( self.extra_files_path )
# TODO: purge metadata files
self.deleted = True
self.purged = True
@@ -2510,6 +2520,16 @@ class DatasetInstance(object):
"""Detects whether there is any data"""
return self.dataset.has_data()
def get_created_from_basename(self):
return self.dataset.created_from_basename
def set_created_from_basename(self, created_from_basename):
if self.dataset.created_from_basename is not None:
raise Exception("Underlying dataset already has a created_from_basename set.")
self.dataset.created_from_basename = created_from_basename
created_from_basename = property(get_created_from_basename, set_created_from_basename)
def get_raw_data(self):
"""Returns the full data. To stream it open the file_name and read/write as needed"""
return self.datatype.get_raw_data(self)
@@ -3393,6 +3413,7 @@ class LibraryDataset(RepresentById):
state=ldda.state,
name=ldda.name,
file_name=ldda.file_name,
created_from_basename=ldda.created_from_basename,
uploaded_by=ldda.user.email,
message=ldda.message,
date_uploaded=ldda.create_time.isoformat(),
@@ -3545,7 +3566,8 @@ class LibraryDatasetDatasetAssociation(DatasetInstance, HasName, RepresentById):
data_type=ldda.datatype.__class__.__module__ + '.' + ldda.datatype.__class__.__name__,
genome_build=ldda.dbkey,
misc_info=ldda.info,
misc_blurb=ldda.blurb)
misc_blurb=ldda.blurb,
created_from_basename=ldda.created_from_basename)
if ldda.dataset.uuid is None:
rval['uuid'] = None
else:
+1
View File
@@ -263,6 +263,7 @@ model.Dataset.table = Table(
Column("object_store_id", TrimmedString(255), index=True),
Column("external_filename", TEXT),
Column("_extra_files_path", TEXT),
Column("created_from_basename", TEXT),
Column('file_size', Numeric(15, 0)),
Column('total_size', Numeric(15, 0)),
Column('uuid', UUIDType()))
@@ -6,9 +6,10 @@ from __future__ import print_function
import logging
from sqlalchemy import Column, MetaData, Table
from sqlalchemy import Column, MetaData
from galaxy.model.custom_types import UUIDType
from galaxy.model.migrate.versions.util import add_column, drop_column
log = logging.getLogger(__name__)
@@ -19,14 +20,8 @@ def upgrade(migrate_engine):
metadata.bind = migrate_engine
metadata.reflect()
metadata_file_table = Table("metadata_file", metadata, autoload=True)
try:
uuid_column = Column('uuid', UUIDType())
uuid_column.create(metadata_file_table)
assert uuid_column is metadata_file_table.c.uuid
except Exception:
log.exception("Adding column 'uuid' to `MetadataFile` table failed.")
uuid_column = Column('uuid', UUIDType())
add_column(uuid_column, 'metadata_file', metadata)
def downgrade(migrate_engine):
@@ -34,10 +29,4 @@ def downgrade(migrate_engine):
metadata.bind = migrate_engine
metadata.reflect()
metadata_file_table = Table("metadata_file", metadata, autoload=True)
try:
column = metadata_file_table.c.uuid
column.drop()
except Exception:
log.exception("Dropping 'uuid' column from `metadata_file` table failed.")
drop_column('uuid', 'metadata_file', metadata)
@@ -0,0 +1,29 @@
"""
Adds created_from_basename to dataset.
"""
import datetime
import logging
from sqlalchemy import Column, MetaData, TEXT
from galaxy.model.migrate.versions.util import add_column, drop_column
now = datetime.datetime.utcnow
log = logging.getLogger(__name__)
metadata = MetaData()
def upgrade(migrate_engine):
metadata.bind = migrate_engine
print(__doc__)
metadata.reflect()
created_from_basename_column = Column("created_from_basename", TEXT, default=None)
add_column(created_from_basename_column, 'dataset', metadata)
def downgrade(migrate_engine):
metadata.bind = migrate_engine
metadata.reflect()
drop_column('created_from_basename', 'dataset', metadata)
+16
View File
@@ -55,6 +55,7 @@ class ModelPersistenceContext(object):
tag_list=[],
sources=[],
hashes=[],
created_from_basename=None,
):
sa_session = self.sa_session
@@ -116,6 +117,9 @@ class ModelPersistenceContext(object):
hash_object.hash_value = hash_dict["hash_value"]
primary_data.dataset.hashes.append(hash_object)
if created_from_basename is not None:
primary_data.created_from_basename = created_from_basename
self.flush()
if tag_list:
@@ -204,6 +208,7 @@ class ModelPersistenceContext(object):
sources = discovered_file.match.sources
hashes = discovered_file.match.hashes
created_from_basename = discovered_file.match.created_from_basename
dataset = self.create_dataset(
ext=ext,
@@ -217,6 +222,7 @@ class ModelPersistenceContext(object):
tag_list=tag_list,
sources=sources,
hashes=hashes,
created_from_basename=created_from_basename,
)
log.debug(
"(%s) Created dynamic collection dataset for path [%s] with element identifier [%s] for output [%s] %s",
@@ -475,6 +481,8 @@ def persist_elements_to_folder(model_persistence_context, elements, library_fold
sources = fields_match.sources
hashes = fields_match.hashes
created_from_basename = fields_match.created_from_basename
model_persistence_context.create_dataset(
ext=ext,
designation=designation,
@@ -487,6 +495,7 @@ def persist_elements_to_folder(model_persistence_context, elements, library_fold
link_data=link_data,
sources=sources,
hashes=hashes,
created_from_basename=created_from_basename,
)
@@ -517,6 +526,8 @@ def persist_hdas(elements, model_persistence_context):
sources = fields_match.sources
hashes = fields_match.hashes
created_from_basename = fields_match.created_from_basename
dataset = model_persistence_context.create_dataset(
ext=ext,
designation=designation,
@@ -529,6 +540,7 @@ def persist_hdas(elements, model_persistence_context):
primary_data=primary_dataset,
sources=sources,
hashes=hashes,
created_from_basename=created_from_basename,
)
dataset.raw_set_dataset_state('ok')
if not hda_id:
@@ -701,6 +713,10 @@ class JsonCollectedDatasetMatch(object):
def hashes(self):
return self.as_dict.get("hashes", [])
@property
def created_from_basename(self):
return self.as_dict.get("created_from_basename")
class RegexCollectedDatasetMatch(JsonCollectedDatasetMatch):
+4 -1
View File
@@ -467,7 +467,10 @@ class DiskObjectStore(ObjectStore):
# construct and return hashed path
if os.path.exists(path):
return path
return self._construct_path(obj, **kwargs)
path = self._construct_path(obj, **kwargs)
if not os.path.exists(path):
raise ObjectNotFound
return path
def update_from_file(self, obj, file_name=None, create=False, **kwargs):
"""`create` parameter is not used in this implementation."""
+8 -39
View File
@@ -5,13 +5,10 @@ This parallelizes the task over available cores using multiprocessing.
Code mostly taken form CloudBioLinux.
"""
import contextlib
import functools
import glob
import multiprocessing
import os
import subprocess
from multiprocessing.pool import IMapIterator
import threading
try:
import boto
@@ -20,13 +17,6 @@ except ImportError:
boto = None
def map_wrap(f):
@functools.wraps(f)
def wrapper(args):
return f(*args)
return wrapper
def mp_from_ids(s3server, mp_id, mp_keyname, mp_bucketname):
"""Get the multipart upload from the bucket and multipart IDs.
@@ -51,7 +41,6 @@ def mp_from_ids(s3server, mp_id, mp_keyname, mp_bucketname):
return mp
@map_wrap
def transfer_part(s3server, mp_id, mp_keyname, mp_bucketname, i, part):
"""Transfer a part of a multipart upload. Designed to be run in parallel.
"""
@@ -64,8 +53,6 @@ def transfer_part(s3server, mp_id, mp_keyname, mp_bucketname, i, part):
def multipart_upload(s3server, bucket, s3_key_name, tarball, mb_size):
"""Upload large files using Amazon's multipart upload functionality.
"""
cores = multiprocessing.cpu_count()
def split_file(in_file, mb_size, split_num=5):
prefix = os.path.join(os.path.dirname(in_file),
"%sS3PART" % (os.path.basename(s3_key_name)))
@@ -80,29 +67,11 @@ def multipart_upload(s3server, bucket, s3_key_name, tarball, mb_size):
mp = bucket.initiate_multipart_upload(s3_key_name,
reduced_redundancy=s3server['use_rr'])
with multimap(cores) as pmap:
for _ in pmap(transfer_part, ((s3server, mp.id, mp.key_name, mp.bucket_name, i, part)
for (i, part) in
enumerate(split_file(tarball, mb_size, cores)))):
pass
for (i, part) in enumerate(split_file(tarball, mb_size)):
t = threading.Thread(
target=transfer_part,
args=(s3server, mp.id, mp.key_name, mp.bucket_name, i, part))
t.start()
t.join()
mp.complete_upload()
@contextlib.contextmanager
def multimap(cores=None):
"""Provide multiprocessing imap like function.
The context manager handles setting up the pool, worked around interrupt issues
and terminating the pool on completion.
"""
if cores is None:
cores = max(multiprocessing.cpu_count() - 1, 1)
def wrapper(func):
def wrap(self, timeout=None):
return func(self, timeout=timeout if timeout is not None else 1e100)
return wrap
IMapIterator.next = wrapper(IMapIterator.next)
pool = multiprocessing.Pool(cores)
yield pool.imap
pool.terminate()
+10 -1
View File
@@ -464,6 +464,7 @@ class Tool(Dictifiable):
self.guid = guid
self.old_id = None
self.version = None
self.python_template_version = None
self._lineage = None
self.dependencies = []
# populate toolshed repository info, if available
@@ -643,6 +644,14 @@ class Tool(Dictifiable):
message = template % (self.id, self.profile)
raise Exception(message)
self.python_template_version = tool_source.parse_python_template_version()
if self.python_template_version is None:
# If python_template_version not specified we assume tools with profile versions >= 19.05 are python 3 ready
if self.profile >= 19.05:
self.python_template_version = '3.5'
else:
self.python_template_version = '2.7'
# Get the (user visible) name of the tool
self.name = tool_source.parse_name()
if not self.name and dynamic:
@@ -1065,7 +1074,7 @@ class Tool(Dictifiable):
if citation_elem.tag != "citation":
pass
if hasattr(self.app, 'citations_manager'):
citation = self.app.citations_manager.parse_citation(citation_elem, self.tool_dir)
citation = self.app.citations_manager.parse_citation(citation_elem)
if citation:
citations.append(citation)
return citations
+11 -5
View File
@@ -1,5 +1,6 @@
import json
import logging
import os
import re
from json import dumps
@@ -377,7 +378,8 @@ class DefaultToolAction(object):
wrapped_params.params,
inp_data,
inp_dataset_collections,
input_ext
input_ext,
python_template_version=tool.python_template_version,
)
create_datasets = True
dataset = None
@@ -391,6 +393,10 @@ class DefaultToolAction(object):
break
data = app.model.HistoryDatasetAssociation(extension=ext, dataset=dataset, create_dataset=create_datasets, flush=False)
if create_datasets:
from_work_dir = output.from_work_dir
if from_work_dir is not None:
data.dataset.created_from_basename = os.path.basename(from_work_dir)
if hidden is None:
hidden = output.hidden
if not hidden and dataset_collection_elements is not None: # Mapping over a collection - hide datasets
@@ -744,7 +750,7 @@ class DefaultToolAction(object):
if output.label:
params['tool'] = tool
params['on_string'] = on_text
return fill_template(output.label, context=params)
return fill_template(output.label, context=params, python_template_version=tool.python_template_version)
else:
return self._get_default_data_name(dataset, tool, on_text=on_text, trans=trans, incoming=incoming, history=history, params=params, job_params=job_params)
@@ -761,7 +767,7 @@ class DefaultToolAction(object):
if output.actions:
for action in output.actions.actions:
if action.tag == "metadata" and action.default:
metadata_new_value = fill_template(action.default, context=params).split(",")
metadata_new_value = fill_template(action.default, context=params, python_template_version=tool.python_template_version).split(",")
dataset.metadata.__setattr__(str(action.name), metadata_new_value)
def _get_default_data_name(self, dataset, tool, on_text=None, trans=None, incoming=None, history=None, params=None, job_params=None, **kwd):
@@ -924,7 +930,7 @@ def get_ext_or_implicit_ext(hda):
return hda.ext
def determine_output_format(output, parameter_context, input_datasets, input_dataset_collections, random_input_ext):
def determine_output_format(output, parameter_context, input_datasets, input_dataset_collections, random_input_ext, python_template_version='3'):
""" Determines the output format for a dataset based on an abstract
description of the output (galaxy.tools.parser.ToolOutput), the parameter
wrappers, a map of the input datasets (name => HDA), and the last input
@@ -986,7 +992,7 @@ def determine_output_format(output, parameter_context, input_datasets, input_dat
if '$' not in check:
# allow a simple name or more complex specifications
check = '${%s}' % check
if str(fill_template(check, context=parameter_context)) == when_elem.get('value', None):
if fill_template(check, context=parameter_context, python_template_version=python_template_version) == when_elem.get('value', None):
ext = when_elem.get('format', ext)
except Exception: # bad tag input value; possibly referencing a param within a different conditional when block or other nonexistent grouping construct
continue
+3
View File
@@ -108,6 +108,7 @@ def _fetch_target(upload_config, target):
dbkey = item.get("dbkey", "?")
requested_ext = item.get("ext", "auto")
info = item.get("info", None)
created_from_basename = item.get("created_from_basename", None)
tags = item.get("tags", [])
object_id = item.get("object_id", None)
link_data_only = upload_config.link_data_only
@@ -162,6 +163,8 @@ def _fetch_target(upload_config, target):
rval["object_id"] = object_id
if tags:
rval["tags"] = tags
if created_from_basename:
rval["created_from_basename"] = created_from_basename
return rval
elements = elements_tree_map(_resolve_src, items)
+2
View File
@@ -206,7 +206,9 @@ class ContainerRegistry(object):
if self.enable_beta_mulled_containers:
default_resolvers.extend([
CachedMulledDockerContainerResolver(self.app_info, namespace="biocontainers"),
CachedMulledDockerContainerResolver(self.app_info, namespace="local"),
CachedMulledSingularityContainerResolver(self.app_info, namespace="biocontainers"),
CachedMulledSingularityContainerResolver(self.app_info, namespace="local"),
MulledDockerContainerResolver(self.app_info, namespace="biocontainers"),
MulledSingularityContainerResolver(self.app_info, namespace="biocontainers"),
BuildMulledDockerContainerResolver(self.app_info),
+2 -2
View File
@@ -469,7 +469,7 @@ class ToolEvaluator(object):
return
try:
# Substituting parameters into the command
command_line = fill_template(command, context=param_dict)
command_line = fill_template(command, context=param_dict, python_template_version=self.tool.python_template_version)
cleaned_command_line = []
# Remove leading and trailing whitespace from each line for readability.
for line in command_line.split('\n'):
@@ -575,7 +575,7 @@ class ToolEvaluator(object):
def __write_workdir_file(self, config_filename, content, context, is_template=True):
if is_template:
value = fill_template(content, context=context)
value = fill_template(content, context=context, python_template_version=self.tool.python_template_version)
else:
value = unicodify(content)
with io.open(config_filename, "w", encoding='utf-8') as f:
@@ -316,6 +316,7 @@ def collect_primary_datasets(job_context, output, input_ext):
extra_files_path = new_primary_datasets_attributes.get('extra_files', None)
if extra_files_path:
extra_files_path_joined = os.path.join(job_working_directory, extra_files_path)
primary_data.dataset.create_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)
+3
View File
@@ -150,6 +150,9 @@ class CwlToolSource(ToolSource):
def parse_profile(self):
return "16.04"
def parse_python_template_version(self):
return '3.5'
class CwlPageSource(PageSource):
+6
View File
@@ -218,6 +218,12 @@ class ToolSource(object):
""" Return tool profile version as Galaxy major e.g. 16.01 or 16.04.
"""
@abstractmethod
def parse_python_template_version(self):
"""
Return minimum python version that the tool template has been developed against.
"""
def macro_paths(self):
return []
+8
View File
@@ -5,6 +5,8 @@ import traceback
import uuid
from math import isinf
import packaging.version
from galaxy.tools.deps import requirements
from galaxy.util import string_as_bool, xml_text, xml_to_string
from galaxy.util.odict import odict
@@ -483,6 +485,12 @@ class XmlToolSource(ToolSource):
# - Enable buggy interpreter attribute.
return self.root.get("profile", "16.01")
def parse_python_template_version(self):
python_template_version = self.root.get("python_template_version", None)
if python_template_version is not None:
python_template_version = packaging.version.parse(python_template_version)
return python_template_version
def _test_elem_to_dict(test_elem, i):
rval = dict(
+8
View File
@@ -1,3 +1,5 @@
import packaging.version
from galaxy.tools.deps import requirements
from galaxy.util.odict import odict
from .interface import InputSource
@@ -162,6 +164,12 @@ class YamlToolSource(ToolSource):
def parse_profile(self):
return self.root_dict.get("profile", "16.04")
def parse_python_template_version(self):
python_template_version = self.root_dict.get("python_template_version", None)
if python_template_version is not None:
python_template_version = packaging.version.parse(python_template_version)
return python_template_version
def _parse_test(i, test_dict):
inputs = test_dict["inputs"]
+12 -3
View File
@@ -24,10 +24,19 @@ def assert_has_h5_attribute(output_bytes, key, value):
def assert_has_h5_keys(output_bytes, keys):
""" Asserts the specified HDF5 output has exactly the given keys."""
""" Asserts the specified HDF5 output has the given keys."""
_assert_h5py()
keys = [k.strip() for k in keys.strip().split(',')]
h5_keys = sorted(keys)
output_temp = io.BytesIO(output_bytes)
local_keys = sorted(list(h5py.File(output_temp, 'r').keys()))
assert local_keys == h5_keys, "Not a HDF5 file or H5 keys do not match:\n\t%s\n\t%s" % (local_keys, h5_keys)
local_keys = []
def append_keys(key):
local_keys.append(key)
return None
h5py.File(output_temp, 'r').visit(append_keys)
missing = 0
for key in h5_keys:
if key not in local_keys:
missing += 1
assert missing == 0, "Not a HDF5 file or H5 keys missing:\n\t%s\n\t%s" % (local_keys, h5_keys)

Some files were not shown because too many files have changed in this diff Show More