mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-25 00:12:08 +08:00
@@ -57,6 +57,7 @@
|
||||
:is-history-item="isHistoryItem"
|
||||
:is-visible="item.visible"
|
||||
:state="state"
|
||||
:item-urls="itemUrls"
|
||||
@delete="$emit('delete')"
|
||||
@display="onDisplay"
|
||||
@edit="onEdit"
|
||||
@@ -85,6 +86,7 @@
|
||||
v-if="expandDataset"
|
||||
:dataset="item"
|
||||
:show-highlight="isHistoryItem"
|
||||
:item-urls="itemUrls"
|
||||
@edit="onEdit"
|
||||
@toggleHighlights="toggleHighlights" />
|
||||
</b-collapse>
|
||||
@@ -169,6 +171,26 @@ export default {
|
||||
tagsDisabled() {
|
||||
return !this.expandDataset || !this.isHistoryItem;
|
||||
},
|
||||
isCollection() {
|
||||
return "collection_type" in this.item;
|
||||
},
|
||||
/** Relative URLs for history item actions */
|
||||
itemUrls() {
|
||||
const id = this.item.id;
|
||||
if (this.isCollection) {
|
||||
return {
|
||||
edit: `/collection/edit/${id}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
display: `/datasets/${id}/display/?preview=True`,
|
||||
edit: `/datasets/edit/${id}`,
|
||||
showDetails: `/datasets/${id}/details`,
|
||||
reportError: `/datasets/error?dataset_id=${id}`,
|
||||
rerun: `/tool_runner/rerun?id=${id}`,
|
||||
visualize: `/visualizations?dataset_id=${id}`,
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
@@ -179,8 +201,7 @@ export default {
|
||||
}
|
||||
},
|
||||
onDisplay() {
|
||||
const url = `datasets/${this.item.id}/display/?preview=True`;
|
||||
iframeAdd({ path: url, title: this.name });
|
||||
iframeAdd({ path: this.itemUrls.display, title: this.name });
|
||||
},
|
||||
onDragStart(evt) {
|
||||
evt.dataTransfer.dropEffect = "move";
|
||||
@@ -188,11 +209,7 @@ export default {
|
||||
evt.dataTransfer.setData("text", JSON.stringify([this.item]));
|
||||
},
|
||||
onEdit() {
|
||||
if (this.item.collection_type) {
|
||||
this.$router.push(`/collection/edit/${this.item.id}`);
|
||||
} else {
|
||||
this.$router.push(`/datasets/edit/${this.item.id}`);
|
||||
}
|
||||
this.$router.push(this.itemUrls.edit);
|
||||
},
|
||||
onTags(newTags) {
|
||||
this.$emit("tag-change", this.item, newTags);
|
||||
|
||||
@@ -4,25 +4,27 @@
|
||||
v-if="isDataset"
|
||||
:disabled="displayDisabled"
|
||||
:title="displayButtonTitle"
|
||||
class="px-1"
|
||||
class="display-btn px-1"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click.stop="$emit('display')">
|
||||
:href="displayUrl"
|
||||
@click.prevent.stop="$emit('display')">
|
||||
<icon icon="eye" />
|
||||
</b-button>
|
||||
<b-button
|
||||
v-if="isHistoryItem"
|
||||
:disabled="editDisabled"
|
||||
:title="editButtonTitle"
|
||||
class="px-1"
|
||||
class="edit-btn px-1"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click.stop="$emit('edit')">
|
||||
:href="editUrl"
|
||||
@click.prevent.stop="$emit('edit')">
|
||||
<icon icon="pen" />
|
||||
</b-button>
|
||||
<b-button
|
||||
v-if="isHistoryItem && !isDeleted"
|
||||
class="px-1"
|
||||
class="delete-btn px-1"
|
||||
title="Delete"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@@ -31,7 +33,7 @@
|
||||
</b-button>
|
||||
<b-button
|
||||
v-if="isHistoryItem && isDeleted"
|
||||
class="px-1"
|
||||
class="undelete-btn px-1"
|
||||
title="Undelete"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@@ -40,7 +42,7 @@
|
||||
</b-button>
|
||||
<b-button
|
||||
v-if="isHistoryItem && !isVisible"
|
||||
class="px-1"
|
||||
class="unhide-btn px-1"
|
||||
title="Unhide"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@@ -51,6 +53,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { prependPath } from "utils/redirect.js";
|
||||
export default {
|
||||
props: {
|
||||
isDataset: { type: Boolean, required: true },
|
||||
@@ -58,6 +61,7 @@ export default {
|
||||
isHistoryItem: { type: Boolean, required: true },
|
||||
isVisible: { type: Boolean, default: true },
|
||||
state: { type: String, default: "" },
|
||||
itemUrls: { type: Object, required: true },
|
||||
},
|
||||
computed: {
|
||||
displayButtonTitle() {
|
||||
@@ -78,6 +82,12 @@ export default {
|
||||
editDisabled() {
|
||||
return ["discarded", "new", "upload", "queued", "running", "waiting"].includes(this.state);
|
||||
},
|
||||
displayUrl() {
|
||||
return prependPath(this.itemUrls.display);
|
||||
},
|
||||
editUrl() {
|
||||
return prependPath(this.itemUrls.edit);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -2,7 +2,14 @@
|
||||
<div class="dataset-actions mb-1">
|
||||
<div class="clearfix">
|
||||
<div class="btn-group float-left">
|
||||
<b-button v-if="showError" class="px-1" title="Error" size="sm" variant="link" @click.stop="onError">
|
||||
<b-button
|
||||
v-if="showError"
|
||||
class="px-1"
|
||||
title="Error"
|
||||
size="sm"
|
||||
variant="link"
|
||||
:href="reportErrorUrl"
|
||||
@click.prevent.stop="onError">
|
||||
<span class="fa fa-bug" />
|
||||
</b-button>
|
||||
<dataset-download v-if="showDownloads" :item="item" @on-download="onDownload" />
|
||||
@@ -21,7 +28,8 @@
|
||||
title="Dataset Details"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click.stop="onInfo">
|
||||
:href="showDetailsUrl"
|
||||
@click.prevent.stop="onInfo">
|
||||
<span class="fa fa-info-circle" />
|
||||
</b-button>
|
||||
<b-button
|
||||
@@ -30,7 +38,8 @@
|
||||
title="Run Job Again"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click.stop="onRerun">
|
||||
:href="rerunUrl"
|
||||
@click.prevent.stop="onRerun">
|
||||
<span class="fa fa-redo" />
|
||||
</b-button>
|
||||
<b-button
|
||||
@@ -39,7 +48,8 @@
|
||||
title="Visualize"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click.stop="onVisualize">
|
||||
:href="visualizeUrl"
|
||||
@click.prevent.stop="onVisualize">
|
||||
<span class="fa fa-bar-chart-o" />
|
||||
</b-button>
|
||||
<b-button
|
||||
@@ -60,9 +70,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { iframeAdd } from "components/plugins/legacyNavigation";
|
||||
import { copy as sendToClipboard } from "utils/clipboard";
|
||||
import { absPath } from "utils/redirect";
|
||||
import { absPath, prependPath } from "utils/redirect.js";
|
||||
import { downloadUrlMixin } from "./mixins.js";
|
||||
import DatasetDownload from "./DatasetDownload";
|
||||
|
||||
@@ -74,6 +83,7 @@ export default {
|
||||
props: {
|
||||
item: { type: Object, required: true },
|
||||
showHighlight: { type: Boolean, default: false },
|
||||
itemUrls: { type: Object, required: true },
|
||||
},
|
||||
computed: {
|
||||
showDownloads() {
|
||||
@@ -97,6 +107,18 @@ export default {
|
||||
// TODO: Check hasViz, if visualizations are activated in the config
|
||||
return !this.item.purged && ["ok", "failed_metadata", "error"].includes(this.item.state);
|
||||
},
|
||||
reportErrorUrl() {
|
||||
return prependPath(this.itemUrls.reportError);
|
||||
},
|
||||
showDetailsUrl() {
|
||||
return prependPath(this.itemUrls.showDetails);
|
||||
},
|
||||
rerunUrl() {
|
||||
return prependPath(this.itemUrls.rerun);
|
||||
},
|
||||
visualizeUrl() {
|
||||
return prependPath(this.itemUrls.visualize);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onCopyLink() {
|
||||
@@ -107,21 +129,26 @@ export default {
|
||||
window.location.href = resource;
|
||||
},
|
||||
onError() {
|
||||
this.$router.push(`/datasets/${this.item.id}/error`);
|
||||
this.$router.push(this.itemUrls.reportError);
|
||||
},
|
||||
onInfo() {
|
||||
this.$router.push(`/datasets/${this.item.id}/details`);
|
||||
this.$router.push(this.itemUrls.showDetails);
|
||||
},
|
||||
onRerun() {
|
||||
this.$router.push(`/root?job_id=${this.item.creating_job}`);
|
||||
},
|
||||
onVisualize() {
|
||||
const name = this.item.name || "...";
|
||||
iframeAdd({
|
||||
title: `Visualization of ${name}`,
|
||||
path: `/visualizations?dataset_id=${this.item.id}`,
|
||||
$router: this.$router,
|
||||
});
|
||||
const name = this.item.name || "";
|
||||
const title = `Visualization of ${name}`;
|
||||
const path = this.itemUrls.visualize;
|
||||
const redirectParams = {
|
||||
path: path,
|
||||
title: title,
|
||||
tryIframe: false,
|
||||
};
|
||||
if (!this.iframeAdd(redirectParams)) {
|
||||
this.$router.push(path);
|
||||
}
|
||||
},
|
||||
onHighlight() {
|
||||
this.$emit("toggleHighlights");
|
||||
|
||||
@@ -21,7 +21,11 @@
|
||||
<span class="value">{{ result.misc_info }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<DatasetActions :item="result" :show-highlight="showHighlight" @toggleHighlights="toggleHighlights" />
|
||||
<DatasetActions
|
||||
:item="result"
|
||||
:show-highlight="showHighlight"
|
||||
:item-urls="itemUrls"
|
||||
@toggleHighlights="toggleHighlights" />
|
||||
<pre v-if="result.peek" class="dataset-peek p-1" v-html="result.peek" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -94,6 +98,7 @@ export default {
|
||||
props: {
|
||||
dataset: { type: Object, required: true },
|
||||
showHighlight: { type: Boolean, default: false },
|
||||
itemUrls: { type: Object, required: true },
|
||||
},
|
||||
computed: {
|
||||
stateText() {
|
||||
|
||||
@@ -14,12 +14,15 @@
|
||||
<template v-slot:button-content>
|
||||
<span class="fa fa-save" />
|
||||
</template>
|
||||
<b-dropdown-item v-localize @click.stop="onDownload(downloadUrl)"> Download Dataset </b-dropdown-item>
|
||||
<b-dropdown-item v-localize :href="downloadUrl" @click.prevent.stop="onDownload(downloadUrl)">
|
||||
Download Dataset
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item
|
||||
v-for="(metaFile, index) of metaFiles"
|
||||
:key="index"
|
||||
:data-description="`download ${metaFile.file_type}`"
|
||||
@click.stop="onDownload(metaDownloadUrl, metaFile.file_type)">
|
||||
:href="metaDownloadUrl + metaFile.file_type"
|
||||
@click.prevent.stop="onDownload(metaDownloadUrl, metaFile.file_type)">
|
||||
Download {{ metaFile.file_type }}
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
@@ -29,7 +32,8 @@
|
||||
title="Download"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click.stop="onDownload(downloadUrl)">
|
||||
:href="downloadUrl"
|
||||
@click.prevent.stop="onDownload(downloadUrl)">
|
||||
<span class="fa fa-save" />
|
||||
</b-button>
|
||||
</template>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<b-dropdown-text id="history-op-all-content">
|
||||
<span v-localize>With entire history...</span>
|
||||
</b-dropdown-text>
|
||||
<b-dropdown-item v-if="numItemsActive" data-description="copy datasets" @click="onCopy">
|
||||
<b-dropdown-item data-description="copy datasets" @click="onCopy">
|
||||
<span v-localize>Copy Datasets</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item v-if="numItemsHidden" v-b-modal:show-all-hidden-content>
|
||||
|
||||
@@ -9,11 +9,13 @@ export const getUserPreferencesModel = (user_id) => {
|
||||
information: {
|
||||
title: _l("Manage Information"),
|
||||
id: "edit-preferences-information",
|
||||
description: _l("Edit your email, addresses and custom parameters or change your public name."),
|
||||
description: config.enable_account_interface
|
||||
? _l("Edit your email, addresses and custom parameters or change your public name.")
|
||||
: _l("Edit your custom parameters."),
|
||||
url: `api/users/${user_id}/information/inputs`,
|
||||
icon: "fa-user",
|
||||
redirect: "user",
|
||||
shouldRender: !config.use_remote_user && config.enable_account_interface,
|
||||
shouldRender: !config.use_remote_user,
|
||||
},
|
||||
password: {
|
||||
title: _l("Change Password"),
|
||||
@@ -69,7 +71,7 @@ export const getUserPreferencesModel = (user_id) => {
|
||||
icon: "fa-cloud",
|
||||
submitTitle: "Create a new Key",
|
||||
submitIcon: "fa-check",
|
||||
shouldRender: config.enable_account_interface,
|
||||
shouldRender: true,
|
||||
},
|
||||
toolbox_filters: {
|
||||
title: _l("Manage Toolbox Filters"),
|
||||
|
||||
@@ -88,7 +88,7 @@ export default {
|
||||
deleteHistory: (history) => this.deleteHistory({ history }),
|
||||
deleteCurrentHistory: () => this.deleteHistory({ history: this.currentHistory }),
|
||||
|
||||
// purge history then clearn currentHistoryId
|
||||
// purge history then clear currentHistoryId
|
||||
purgeHistory: (history) => this.deleteHistory({ history, purge: true }),
|
||||
purgeCurrentHistory: () => this.deleteHistory({ history: this.currentHistory, purge: true }),
|
||||
|
||||
|
||||
@@ -32,7 +32,14 @@ const mutations = {
|
||||
Vue.delete(state.histories, doomed.id);
|
||||
},
|
||||
setHistories(state, newHistories = []) {
|
||||
const currentHistoryId = state.currentHistoryId;
|
||||
const currentHistory = state.histories[currentHistoryId];
|
||||
const newMap = newHistories.reduce((acc, h) => ({ ...acc, [h.id]: h }), {});
|
||||
if (currentHistory) {
|
||||
// The incoming history list contains less information than the current history
|
||||
// so we restore the existing current history since it gets updated regularly anyway
|
||||
newMap[currentHistoryId] = currentHistory;
|
||||
}
|
||||
Vue.set(state, "histories", newMap);
|
||||
},
|
||||
setHistoriesLoading(state, isLoading) {
|
||||
@@ -145,10 +152,8 @@ const actions = {
|
||||
commit("setCurrentHistoryId", history.id);
|
||||
},
|
||||
async setCurrentHistory({ dispatch, getters }, id) {
|
||||
if (id !== getters.currentHistoryId) {
|
||||
const changedHistory = await setCurrentHistoryOnServer(id);
|
||||
return dispatch("selectHistory", changedHistory);
|
||||
}
|
||||
const changedHistory = await setCurrentHistoryOnServer(id);
|
||||
return dispatch("selectHistory", changedHistory);
|
||||
},
|
||||
setHistory({ commit }, history) {
|
||||
commit("setHistory", history);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import defaultStore from "store/index";
|
||||
import { urlData } from "utils/url";
|
||||
import { loadSet } from "utils/setCache";
|
||||
import { getCurrentHistoryFromServer } from "./queries";
|
||||
import { getGalaxyInstance } from "app";
|
||||
|
||||
@@ -52,18 +53,19 @@ export async function watchHistoryOnce(store) {
|
||||
lastUpdateTime = history.update_time;
|
||||
// execute request to obtain recently changed items
|
||||
const params = {
|
||||
v: "dev",
|
||||
limit: limit,
|
||||
q: "update_time-ge",
|
||||
qv: lastRequestDate.toISOString(),
|
||||
v: "dev",
|
||||
view: "detailed",
|
||||
};
|
||||
const paramsString = Object.entries(params)
|
||||
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
|
||||
.join("&");
|
||||
const url = `api/histories/${historyId}/contents?${paramsString}`;
|
||||
// request detailed info only for the expanded datasets
|
||||
const detailedIds = getCurrentlyExpandedHistoryContentIds();
|
||||
if (detailedIds.length) {
|
||||
params["details"] = detailedIds.join(",");
|
||||
}
|
||||
const url = `api/histories/${historyId}/contents`;
|
||||
lastRequestDate = new Date();
|
||||
const payload = await urlData({ url });
|
||||
const payload = await urlData({ url, params });
|
||||
// show warning that not all changes have been obtained
|
||||
if (payload && payload.length == limit) {
|
||||
console.debug(`Reached limit of monitored changes (limit=${limit}).`);
|
||||
@@ -110,3 +112,22 @@ export function rewatchHistory() {
|
||||
watchHistory();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of history item IDs that are currently expanded in the history panel from the cache.
|
||||
* These content items need to retrieve detailed information when updated.
|
||||
* @returns {Array<string>} List of history item IDs that are currently expanded.
|
||||
*/
|
||||
function getCurrentlyExpandedHistoryContentIds() {
|
||||
const expandedItemIds = [];
|
||||
const cacheKey = "expanded-history-items";
|
||||
const expandedItems = loadSet(cacheKey);
|
||||
expandedItems.forEach((key) => {
|
||||
// Items have the format: <type>-<id>
|
||||
const itemId = key.split("-")[1];
|
||||
if (itemId) {
|
||||
expandedItemIds.push(itemId);
|
||||
}
|
||||
});
|
||||
return expandedItemIds;
|
||||
}
|
||||
|
||||
@@ -290,9 +290,8 @@ history_panel:
|
||||
options_use_legacy_history: 'a[data-description="switch to legacy history view"]'
|
||||
|
||||
collection_menu_button: '.collection-menu'
|
||||
collection_menu_edit_attributes:
|
||||
type: xpath
|
||||
selector: '//button[@title="Edit attributes"]'
|
||||
collection_menu_edit_attributes: '.edit-btn'
|
||||
|
||||
new_history_button: '.history-new-button'
|
||||
multi_view_button: '.history-view-multi-button'
|
||||
histories_operation_menu: '[data-description="history options"]'
|
||||
|
||||
@@ -10,15 +10,33 @@ export function reloadPage() {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
// Prepends configured appRoot to given url
|
||||
const slashCleanup = /(\/)+/g;
|
||||
/**
|
||||
* Prepends the configured app root to given url
|
||||
* @param {String} path
|
||||
* @returns The relative URL path with the configured appRoot.
|
||||
*/
|
||||
export function prependPath(path) {
|
||||
const root = getAppRoot();
|
||||
return `${root}/${path}`.replace(slashCleanup, "/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute URL path for this server given a relative path.
|
||||
* @param {String} path
|
||||
* @returns The absolute URL path.
|
||||
*/
|
||||
export function absPath(path) {
|
||||
const relativePath = prependPath(path);
|
||||
const relativePath = hasRoot(path) ? path : prependPath(path);
|
||||
const server = window.location.origin;
|
||||
return `${server}/${relativePath}`.replace(slashCleanup, "/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the path already has the app root.
|
||||
* @param {String} path
|
||||
* @returns true if the given path starts with the app root.
|
||||
*/
|
||||
function hasRoot(path) {
|
||||
return path.startsWith(getAppRoot());
|
||||
}
|
||||
|
||||
@@ -2,11 +2,12 @@ import axios from "axios";
|
||||
import { getAppRoot } from "onload/loadConfig";
|
||||
import { rethrowSimple } from "utils/simple-error";
|
||||
|
||||
export async function urlData({ url, headers }) {
|
||||
export async function urlData({ url, headers, params }) {
|
||||
try {
|
||||
console.debug("Requesting data from: ", url);
|
||||
headers = headers || {};
|
||||
const { data } = await axios.get(`${getAppRoot()}${url}`, { headers });
|
||||
params = params || {};
|
||||
const { data } = await axios.get(`${getAppRoot()}${url}`, { headers, params });
|
||||
return data;
|
||||
} catch (e) {
|
||||
rethrowSimple(e);
|
||||
|
||||
@@ -92,29 +92,29 @@ steps:
|
||||
grey this means you are queued and need to wait until your tool can be started. If your dataset turns into red, an error has occurred. Please report the bug to the Galaxy team with the bug report button."
|
||||
|
||||
- title: "View dataset"
|
||||
element: "#current-history-panel div.content-item button[title='Display']"
|
||||
element: "#current-history-panel div.content-item .display-btn"
|
||||
intro: "View your dataset by clicking the eye button."
|
||||
|
||||
- title: "Rename dataset"
|
||||
element: "#current-history-panel div.content-item button[title='Edit attributes']"
|
||||
element: "#current-history-panel div.content-item .edit-btn"
|
||||
intro: "Rename your dataset by clicking the pencil button."
|
||||
|
||||
- title: "Dataset information"
|
||||
element: "div.content-item .content-title"
|
||||
intro: "This is your dataset. You can get more informations and options like different visualizations by clicking on it."
|
||||
postclick: true
|
||||
|
||||
|
||||
- title: "Remove dataset"
|
||||
element: "#current-history-panel div.content-item button[title='Delete']"
|
||||
element: "#current-history-panel div.content-item .delete-btn"
|
||||
intro: "Delete your dataset by clicking the trash-button."
|
||||
|
||||
- title: "Dataset information"
|
||||
element: "#current-history-panel div.content-item button[title='Dataset Details']"
|
||||
element: "#current-history-panel div.content-item .params-btn"
|
||||
intro: "Clicking on your dataset provides you with more information regarding your dataset (e.g. filetype or size)."
|
||||
preclick: true
|
||||
|
||||
- title: "Re-run tool"
|
||||
element: "#current-history-panel div.content-item button[title='Run Job Again']"
|
||||
element: "#current-history-panel div.content-item .rerun-btn"
|
||||
intro: "By clicking the reload button, you can re-run your tool again (e.g. with different parameters or on another dataset)."
|
||||
|
||||
- title: "Panel collapse"
|
||||
|
||||
@@ -47,26 +47,26 @@ steps:
|
||||
element: "#current-history-panel div.content-item span.datatype"
|
||||
intro: "Galaxy has assigned a datatype to your dataset during upload, which you can see here."
|
||||
|
||||
- element: "#current-history-panel div.content-item button[title='Download']"
|
||||
- element: "#current-history-panel div.content-item .download-btn"
|
||||
title: "Download your dataset"
|
||||
intro: "You can download every dataset by using the floppy disc symbol."
|
||||
|
||||
- element: "#current-history-panel div.content-item button[title='Dataset Details']"
|
||||
- element: "#current-history-panel div.content-item .params-btn"
|
||||
title: "Even more information"
|
||||
intro: "Get an overview of all metadata associated with your dataset by using the Information symbol."
|
||||
preclick: true
|
||||
|
||||
- element: "#current-history-panel div.content-item button[title='Display']"
|
||||
- element: "#current-history-panel div.content-item .display-btn"
|
||||
title: "Inspect your data"
|
||||
intro: "The eye symbol can be used to look at your data."
|
||||
preclick: true
|
||||
|
||||
- element: "#current-history-panel div.content-item button[title='Edit attributes']"
|
||||
- element: "#current-history-panel div.content-item .edit-btn"
|
||||
title: "Edit metadata"
|
||||
intro: "With the pencil button you can edit metadata attributes of your dataset, like the associated filetype or the dataset name."
|
||||
preclick: true
|
||||
|
||||
- element: "#current-history-panel div.content-item button[title='Delete']"
|
||||
- element: "#current-history-panel div.content-item .delete-btn"
|
||||
title: "Remove datasets"
|
||||
intro: "You can remove a dataset from the history with the trash can symbol."
|
||||
postclick: true
|
||||
@@ -76,7 +76,7 @@ steps:
|
||||
intro: "By default your history will hide all deleted datasets from you. You can visualize them by filtering."
|
||||
textinsert: "deleted:true"
|
||||
|
||||
- element: "#current-history-panel div.content-item button[title='Undelete']"
|
||||
- element: "#current-history-panel div.content-item .undelete-btn"
|
||||
title: "Undeleting a dataset"
|
||||
intro: |
|
||||
Galaxy datasets are only marked as deleted and can be recovered by clicking this link.
|
||||
@@ -95,4 +95,3 @@ steps:
|
||||
|
||||
- title: "Enjoy your Galaxy Histories"
|
||||
intro: "Thanks for taking this tour! Happy research with Galaxy!"
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ steps:
|
||||
- element: "#right"
|
||||
intro: "This is your history. It contains all datasets you are currently working with including our uploaded table."
|
||||
|
||||
- element: "#current-history-panel div.content-item button[title='Display']"
|
||||
- element: "#current-history-panel div.content-item .display-btn"
|
||||
intro: "Clicking the eye-icon usually displays a dataset in the center panel."
|
||||
postclick: true
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
if (tool_id === "upload1" || tool_id === "upload") {
|
||||
document.getElementById("tool-panel-upload-button").click();
|
||||
} else {
|
||||
Galaxy.router.push("/", { tool_id: tool_id });
|
||||
Galaxy.router.push("/", { tool_id: encodeURI(tool_id) });
|
||||
}
|
||||
removeOverlay();
|
||||
});
|
||||
|
||||
@@ -3049,11 +3049,20 @@
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:Description:
|
||||
Boosts are used to customize this instance's toolbox search. The
|
||||
higher the boost, the more importance the scoring algorithm gives
|
||||
to the given field. Section refers to the tool group in the tool
|
||||
panel. Rest of the fields are tool's attributes.
|
||||
:Default: ``9.0``
|
||||
In tool search, a query match against a tool's name text will
|
||||
receive this score multiplier.
|
||||
:Default: ``20.0``
|
||||
:Type: float
|
||||
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
``tool_name_exact_multiplier``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:Description:
|
||||
If a search query matches a tool name exactly, the score will be
|
||||
multiplied by this factor.
|
||||
:Default: ``10.0``
|
||||
:Type: float
|
||||
|
||||
|
||||
@@ -3062,11 +3071,10 @@
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
:Description:
|
||||
Boosts are used to customize this instance's toolbox search. The
|
||||
higher the boost, the more importance the scoring algorithm gives
|
||||
to the given field. Section refers to the tool group in the tool
|
||||
panel. Rest of the fields are tool's attributes.
|
||||
:Default: ``9.0``
|
||||
In tool search, a query match against a tool's ID text will
|
||||
receive this score multiplier. The query must be an exact match
|
||||
against ID in order to be counted as a match.
|
||||
:Default: ``20.0``
|
||||
:Type: float
|
||||
|
||||
|
||||
@@ -3075,10 +3083,8 @@
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:Description:
|
||||
Boosts are used to customize this instance's toolbox search. The
|
||||
higher the boost, the more importance the scoring algorithm gives
|
||||
to the given field. Section refers to the tool group in the tool
|
||||
panel. Rest of the fields are tool's attributes.
|
||||
In tool search, a query match against a tool's section text will
|
||||
receive this score multiplier.
|
||||
:Default: ``3.0``
|
||||
:Type: float
|
||||
|
||||
@@ -3088,11 +3094,9 @@
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:Description:
|
||||
Boosts are used to customize this instance's toolbox search. The
|
||||
higher the boost, the more importance the scoring algorithm gives
|
||||
to the given field. Section refers to the tool group in the tool
|
||||
panel. Rest of the fields are tool's attributes.
|
||||
:Default: ``2.0``
|
||||
In tool search, a query match against a tool's description text
|
||||
will receive this score multiplier.
|
||||
:Default: ``8.0``
|
||||
:Type: float
|
||||
|
||||
|
||||
@@ -3101,10 +3105,8 @@
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:Description:
|
||||
Boosts are used to customize this instance's toolbox search. The
|
||||
higher the boost, the more importance the scoring algorithm gives
|
||||
to the given field. Section refers to the tool group in the tool
|
||||
panel. Rest of the fields are tool's attributes.
|
||||
In tool search, a query match against a tool's label text will
|
||||
receive this score multiplier.
|
||||
:Default: ``1.0``
|
||||
:Type: float
|
||||
|
||||
@@ -3114,11 +3116,10 @@
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:Description:
|
||||
Boosts are used to customize this instance's toolbox search. The
|
||||
higher the boost, the more importance the scoring algorithm gives
|
||||
to the given field. Section refers to the tool group in the tool
|
||||
panel. Rest of the fields are tool's attributes.
|
||||
:Default: ``5.0``
|
||||
A stub is parsed from the GUID as "owner/repo/tool_id". In tool
|
||||
search, a query match against a tool's stub text will receive this
|
||||
score multiplier.
|
||||
:Default: ``2.0``
|
||||
:Type: float
|
||||
|
||||
|
||||
@@ -3127,10 +3128,22 @@
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:Description:
|
||||
Boosts are used to customize this instance's toolbox search. The
|
||||
higher the boost, the more importance the scoring algorithm gives
|
||||
to the given field. Section refers to the tool group in the tool
|
||||
panel. Rest of the fields are tool's attributes.
|
||||
In tool search, a query match against a tool's help text will
|
||||
receive this score multiplier.
|
||||
:Default: ``1.0``
|
||||
:Type: float
|
||||
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
``tool_help_bm25f_k1``
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:Description:
|
||||
The lower this parameter, the greater the diminishing reward for
|
||||
term frequency in the help text. A higher K1 increases the level
|
||||
of reward for additional occurences of a term. The default value
|
||||
will provide a slight increase in score for the first, second and
|
||||
third occurrence and little reward thereafter.
|
||||
:Default: ``0.5``
|
||||
:Type: float
|
||||
|
||||
@@ -3140,8 +3153,8 @@
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:Description:
|
||||
Limits the number of results in toolbox search. Can be used to
|
||||
tweak how many results will appear.
|
||||
Limits the number of results in toolbox search. Use to set the
|
||||
maximum number of tool search results to display.
|
||||
:Default: ``20``
|
||||
:Type: int
|
||||
|
||||
@@ -3151,9 +3164,11 @@
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:Description:
|
||||
Enable/ disable Ngram-search for tools. It makes tool search
|
||||
results tolerant for spelling mistakes in the query by dividing
|
||||
the query into multiple ngrams and search for each ngram
|
||||
Disabling this will prevent partial matches on tool names.
|
||||
Enable/disable Ngram-search for tools. It makes tool search
|
||||
results tolerant for spelling mistakes in the query, and will also
|
||||
match query substrings e.g. "genome" will match "genomics" or
|
||||
"metagenome".
|
||||
:Default: ``true``
|
||||
:Type: bool
|
||||
|
||||
@@ -3163,7 +3178,7 @@
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:Description:
|
||||
Set minimum size of ngrams
|
||||
Set minimum character length of ngrams
|
||||
:Default: ``3``
|
||||
:Type: int
|
||||
|
||||
@@ -3173,11 +3188,23 @@
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:Description:
|
||||
Set maximum size of ngrams
|
||||
Set maximum character length of ngrams
|
||||
:Default: ``4``
|
||||
:Type: int
|
||||
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
``tool_ngram_factor``
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:Description:
|
||||
Ngram matched scores will be multiplied by this factor. Should
|
||||
always be below 1, because an ngram match is a partial match of a
|
||||
search term.
|
||||
:Default: ``0.2``
|
||||
:Type: float
|
||||
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
``tool_test_data_directories``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1565,63 +1565,69 @@ galaxy:
|
||||
# Available formats are currently 'zip', 'gz', and 'bz2'.
|
||||
#disable_library_comptypes: null
|
||||
|
||||
# Boosts are used to customize this instance's toolbox search. The
|
||||
# higher the boost, the more importance the scoring algorithm gives to
|
||||
# the given field. Section refers to the tool group in the tool
|
||||
# panel. Rest of the fields are tool's attributes.
|
||||
#tool_name_boost: 9.0
|
||||
# In tool search, a query match against a tool's name text will
|
||||
# receive this score multiplier.
|
||||
#tool_name_boost: 20.0
|
||||
|
||||
# Boosts are used to customize this instance's toolbox search. The
|
||||
# higher the boost, the more importance the scoring algorithm gives to
|
||||
# the given field. Section refers to the tool group in the tool
|
||||
# panel. Rest of the fields are tool's attributes.
|
||||
#tool_id_boost: 9.0
|
||||
# If a search query matches a tool name exactly, the score will be
|
||||
# multiplied by this factor.
|
||||
#tool_name_exact_multiplier: 10.0
|
||||
|
||||
# Boosts are used to customize this instance's toolbox search. The
|
||||
# higher the boost, the more importance the scoring algorithm gives to
|
||||
# the given field. Section refers to the tool group in the tool
|
||||
# panel. Rest of the fields are tool's attributes.
|
||||
# In tool search, a query match against a tool's ID text will receive
|
||||
# this score multiplier. The query must be an exact match against ID
|
||||
# in order to be counted as a match.
|
||||
#tool_id_boost: 20.0
|
||||
|
||||
# In tool search, a query match against a tool's section text will
|
||||
# receive this score multiplier.
|
||||
#tool_section_boost: 3.0
|
||||
|
||||
# Boosts are used to customize this instance's toolbox search. The
|
||||
# higher the boost, the more importance the scoring algorithm gives to
|
||||
# the given field. Section refers to the tool group in the tool
|
||||
# panel. Rest of the fields are tool's attributes.
|
||||
#tool_description_boost: 2.0
|
||||
# In tool search, a query match against a tool's description text will
|
||||
# receive this score multiplier.
|
||||
#tool_description_boost: 8.0
|
||||
|
||||
# Boosts are used to customize this instance's toolbox search. The
|
||||
# higher the boost, the more importance the scoring algorithm gives to
|
||||
# the given field. Section refers to the tool group in the tool
|
||||
# panel. Rest of the fields are tool's attributes.
|
||||
# In tool search, a query match against a tool's label text will
|
||||
# receive this score multiplier.
|
||||
#tool_label_boost: 1.0
|
||||
|
||||
# Boosts are used to customize this instance's toolbox search. The
|
||||
# higher the boost, the more importance the scoring algorithm gives to
|
||||
# the given field. Section refers to the tool group in the tool
|
||||
# panel. Rest of the fields are tool's attributes.
|
||||
#tool_stub_boost: 5.0
|
||||
# A stub is parsed from the GUID as "owner/repo/tool_id". In tool
|
||||
# search, a query match against a tool's stub text will receive this
|
||||
# score multiplier.
|
||||
#tool_stub_boost: 2.0
|
||||
|
||||
# Boosts are used to customize this instance's toolbox search. The
|
||||
# higher the boost, the more importance the scoring algorithm gives to
|
||||
# the given field. Section refers to the tool group in the tool
|
||||
# panel. Rest of the fields are tool's attributes.
|
||||
#tool_help_boost: 0.5
|
||||
# In tool search, a query match against a tool's help text will
|
||||
# receive this score multiplier.
|
||||
#tool_help_boost: 1.0
|
||||
|
||||
# Limits the number of results in toolbox search. Can be used to
|
||||
# tweak how many results will appear.
|
||||
# The lower this parameter, the greater the diminishing reward for
|
||||
# term frequency in the help text. A higher K1 increases the level of
|
||||
# reward for additional occurences of a term. The default value will
|
||||
# provide a slight increase in score for the first, second and third
|
||||
# occurrence and little reward thereafter.
|
||||
#tool_help_bm25f_k1: 0.5
|
||||
|
||||
# Limits the number of results in toolbox search. Use to set the
|
||||
# maximum number of tool search results to display.
|
||||
#tool_search_limit: 20
|
||||
|
||||
# Enable/ disable Ngram-search for tools. It makes tool search results
|
||||
# tolerant for spelling mistakes in the query by dividing the query
|
||||
# into multiple ngrams and search for each ngram
|
||||
# Disabling this will prevent partial matches on tool names.
|
||||
# Enable/disable Ngram-search for tools. It makes tool search results
|
||||
# tolerant for spelling mistakes in the query, and will also match
|
||||
# query substrings e.g. "genome" will match "genomics" or
|
||||
# "metagenome".
|
||||
#tool_enable_ngram_search: true
|
||||
|
||||
# Set minimum size of ngrams
|
||||
# Set minimum character length of ngrams
|
||||
#tool_ngram_minsize: 3
|
||||
|
||||
# Set maximum size of ngrams
|
||||
# Set maximum character length of ngrams
|
||||
#tool_ngram_maxsize: 4
|
||||
|
||||
# Ngram matched scores will be multiplied by this factor. Should
|
||||
# always be below 1, because an ngram match is a partial match of a
|
||||
# search term.
|
||||
#tool_ngram_factor: 0.2
|
||||
|
||||
# Set tool test data directory. The test framework sets this value to
|
||||
# 'test-data,https://github.com/galaxyproject/galaxy-test-data.git'
|
||||
# which will cause Galaxy to clone down extra test data on the fly for
|
||||
|
||||
@@ -2206,25 +2206,31 @@ mapping:
|
||||
|
||||
tool_name_boost:
|
||||
type: float
|
||||
default: 9.0
|
||||
default: 20.0
|
||||
required: false
|
||||
reloadable: true
|
||||
desc: |
|
||||
Boosts are used to customize this instance's toolbox search.
|
||||
The higher the boost, the more importance the scoring algorithm gives to the
|
||||
given field. Section refers to the tool group in the tool panel. Rest of
|
||||
the fields are tool's attributes.
|
||||
In tool search, a query match against a tool's name text will receive
|
||||
this score multiplier.
|
||||
|
||||
tool_name_exact_multiplier:
|
||||
type: float
|
||||
default: 10.0
|
||||
required: false
|
||||
reloadable: true
|
||||
desc: |
|
||||
If a search query matches a tool name exactly, the score will be
|
||||
multiplied by this factor.
|
||||
|
||||
tool_id_boost:
|
||||
type: float
|
||||
default: 9.0
|
||||
default: 20.0
|
||||
required: false
|
||||
reloadable: true
|
||||
desc: |
|
||||
Boosts are used to customize this instance's toolbox search.
|
||||
The higher the boost, the more importance the scoring algorithm gives to the
|
||||
given field. Section refers to the tool group in the tool panel. Rest of
|
||||
the fields are tool's attributes.
|
||||
In tool search, a query match against a tool's ID text will receive
|
||||
this score multiplier. The query must be an exact match against ID
|
||||
in order to be counted as a match.
|
||||
|
||||
tool_section_boost:
|
||||
type: float
|
||||
@@ -2232,21 +2238,17 @@ mapping:
|
||||
required: false
|
||||
reloadable: true
|
||||
desc: |
|
||||
Boosts are used to customize this instance's toolbox search.
|
||||
The higher the boost, the more importance the scoring algorithm gives to the
|
||||
given field. Section refers to the tool group in the tool panel. Rest of
|
||||
the fields are tool's attributes.
|
||||
In tool search, a query match against a tool's section text will
|
||||
receive this score multiplier.
|
||||
|
||||
tool_description_boost:
|
||||
type: float
|
||||
default: 2.0
|
||||
default: 8.0
|
||||
required: false
|
||||
reloadable: true
|
||||
desc: |
|
||||
Boosts are used to customize this instance's toolbox search.
|
||||
The higher the boost, the more importance the scoring algorithm gives to the
|
||||
given field. Section refers to the tool group in the tool panel. Rest of
|
||||
the fields are tool's attributes.
|
||||
In tool search, a query match against a tool's description text will
|
||||
receive this score multiplier.
|
||||
|
||||
tool_label_boost:
|
||||
type: float
|
||||
@@ -2254,32 +2256,39 @@ mapping:
|
||||
required: false
|
||||
reloadable: true
|
||||
desc: |
|
||||
Boosts are used to customize this instance's toolbox search.
|
||||
The higher the boost, the more importance the scoring algorithm gives to the
|
||||
given field. Section refers to the tool group in the tool panel. Rest of
|
||||
the fields are tool's attributes.
|
||||
In tool search, a query match against a tool's label text will
|
||||
receive this score multiplier.
|
||||
|
||||
tool_stub_boost:
|
||||
type: float
|
||||
default: 5.0
|
||||
default: 2.0
|
||||
required: false
|
||||
reloadable: true
|
||||
desc: |
|
||||
Boosts are used to customize this instance's toolbox search.
|
||||
The higher the boost, the more importance the scoring algorithm gives to the
|
||||
given field. Section refers to the tool group in the tool panel. Rest of
|
||||
the fields are tool's attributes.
|
||||
A stub is parsed from the GUID as "owner/repo/tool_id".
|
||||
In tool search, a query match against a tool's stub text will receive
|
||||
this score multiplier.
|
||||
|
||||
tool_help_boost:
|
||||
type: float
|
||||
default: 1.0
|
||||
required: false
|
||||
reloadable: true
|
||||
desc: |
|
||||
In tool search, a query match against a tool's help text will receive
|
||||
this score multiplier.
|
||||
|
||||
tool_help_bm25f_k1:
|
||||
type: float
|
||||
default: 0.5
|
||||
required: false
|
||||
reloadable: true
|
||||
desc: |
|
||||
Boosts are used to customize this instance's toolbox search.
|
||||
The higher the boost, the more importance the scoring algorithm gives to the
|
||||
given field. Section refers to the tool group in the tool panel. Rest of
|
||||
the fields are tool's attributes.
|
||||
The lower this parameter, the greater the diminishing reward for
|
||||
term frequency in the help text. A higher K1 increases the level
|
||||
of reward for additional occurences of a term. The default value will
|
||||
provide a slight increase in score for the first, second and third
|
||||
occurrence and little reward thereafter.
|
||||
|
||||
tool_search_limit:
|
||||
type: int
|
||||
@@ -2287,8 +2296,8 @@ mapping:
|
||||
required: false
|
||||
reloadable: true
|
||||
desc: |
|
||||
Limits the number of results in toolbox search. Can be used to tweak how many
|
||||
results will appear.
|
||||
Limits the number of results in toolbox search. Use to set the
|
||||
maximum number of tool search results to display.
|
||||
|
||||
tool_enable_ngram_search:
|
||||
type: bool
|
||||
@@ -2296,10 +2305,11 @@ mapping:
|
||||
required: false
|
||||
reloadable: true
|
||||
desc: |
|
||||
Enable/ disable Ngram-search for tools. It makes tool
|
||||
search results tolerant for spelling mistakes in the query
|
||||
by dividing the query into multiple ngrams and search for
|
||||
each ngram
|
||||
Disabling this will prevent partial matches on tool names.
|
||||
Enable/disable Ngram-search for tools. It makes tool
|
||||
search results tolerant for spelling mistakes in the query, and will
|
||||
also match query substrings e.g. "genome" will match "genomics" or
|
||||
"metagenome".
|
||||
|
||||
tool_ngram_minsize:
|
||||
type: int
|
||||
@@ -2307,7 +2317,7 @@ mapping:
|
||||
required: false
|
||||
reloadable: true
|
||||
desc: |
|
||||
Set minimum size of ngrams
|
||||
Set minimum character length of ngrams
|
||||
|
||||
tool_ngram_maxsize:
|
||||
type: int
|
||||
@@ -2315,7 +2325,16 @@ mapping:
|
||||
required: false
|
||||
reloadable: true
|
||||
desc: |
|
||||
Set maximum size of ngrams
|
||||
Set maximum character length of ngrams
|
||||
|
||||
tool_ngram_factor:
|
||||
type: float
|
||||
default: 0.2
|
||||
required: false
|
||||
reloadable: true
|
||||
desc: |
|
||||
Ngram matched scores will be multiplied by this factor. Should always
|
||||
be below 1, because an ngram match is a partial match of a search term.
|
||||
|
||||
tool_test_data_directories:
|
||||
type: str
|
||||
|
||||
@@ -191,7 +191,7 @@ class DatasetSerializer(base.ModelSerializer[DatasetManager], deletable.Purgable
|
||||
"extra_files_path": self.serialize_extra_files_path,
|
||||
"permissions": self.serialize_permissions,
|
||||
"total_size": lambda item, key, **context: int(item.get_total_size()),
|
||||
"file_size": lambda item, key, **context: int(item.get_size()),
|
||||
"file_size": lambda item, key, **context: int(item.get_size(calculate_size=False)),
|
||||
}
|
||||
self.serializers.update(serializers)
|
||||
|
||||
@@ -526,9 +526,9 @@ class _UnflattenedMetadataDatasetAssociationSerializer(base.ModelSerializer[T],
|
||||
"extra_files_path": self._proxy_to_dataset(serializer=self.dataset_serializer.serialize_extra_files_path),
|
||||
"permissions": self._proxy_to_dataset(serializer=self.dataset_serializer.serialize_permissions),
|
||||
# TODO: do the sizes proxy accurately/in the same way?
|
||||
"size": lambda item, key, **context: int(item.get_size()),
|
||||
"size": lambda item, key, **context: int(item.get_size(calculate_size=False)),
|
||||
"file_size": lambda item, key, **context: self.serializers["size"](item, key, **context),
|
||||
"nice_size": lambda item, key, **context: item.get_size(nice_size=True),
|
||||
"nice_size": lambda item, key, **context: item.get_size(nice_size=True, calculate_size=False),
|
||||
# common to lddas and hdas - from mapping.py
|
||||
"copied_from_history_dataset_association_id": self.serialize_id,
|
||||
"copied_from_library_dataset_dataset_association_id": self.serialize_id,
|
||||
|
||||
@@ -242,7 +242,7 @@ class LibraryDatasetsManager(datasets.DatasetAssociationManager):
|
||||
rval["deleted"] = ld.deleted
|
||||
rval["folder_id"] = f"F{rval['folder_id']}"
|
||||
rval["full_path"] = full_path
|
||||
rval["file_size"] = util.nice_size(int(ldda.get_size()))
|
||||
rval["file_size"] = util.nice_size(int(ldda.get_size(calculate_size=False)))
|
||||
rval["date_uploaded"] = ldda.create_time.isoformat()
|
||||
rval["update_time"] = ldda.update_time.isoformat()
|
||||
rval["can_user_modify"] = trans.user_is_admin or trans.app.security_agent.can_modify_library_item(
|
||||
|
||||
@@ -3569,18 +3569,21 @@ class Dataset(Base, StorableObject, Serializable, _HasTable):
|
||||
else:
|
||||
return self.object_store.size(self)
|
||||
|
||||
def get_size(self, nice_size=False):
|
||||
def get_size(self, nice_size=False, calculate_size=True):
|
||||
"""Returns the size of the data on disk"""
|
||||
if self.file_size:
|
||||
if nice_size:
|
||||
return galaxy.util.nice_size(self.file_size)
|
||||
else:
|
||||
return self.file_size
|
||||
else:
|
||||
elif calculate_size:
|
||||
# Hopefully we only reach this branch in sessionless mode
|
||||
if nice_size:
|
||||
return galaxy.util.nice_size(self._calculate_size())
|
||||
else:
|
||||
return self._calculate_size()
|
||||
else:
|
||||
return self.file_size or 0
|
||||
|
||||
def set_size(self, no_extra_files=False):
|
||||
"""Sets the size of the data on disk.
|
||||
@@ -4021,11 +4024,11 @@ class DatasetInstance(UsesCreateAndUpdateTime, _HasTable):
|
||||
self.clear_associated_files()
|
||||
_get_datatypes_registry().change_datatype(self, new_ext)
|
||||
|
||||
def get_size(self, nice_size=False):
|
||||
def get_size(self, nice_size=False, calculate_size=True):
|
||||
"""Returns the size of the data on disk"""
|
||||
if nice_size:
|
||||
return galaxy.util.nice_size(self.dataset.get_size())
|
||||
return self.dataset.get_size()
|
||||
return galaxy.util.nice_size(self.dataset.get_size(calculate_size=calculate_size))
|
||||
return self.dataset.get_size(calculate_size=calculate_size)
|
||||
|
||||
def set_size(self, **kwds):
|
||||
"""Sets and gets the size of the data on disk"""
|
||||
|
||||
@@ -14,7 +14,10 @@ from alembic.script import ScriptDirectory
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.engine import Engine
|
||||
|
||||
from galaxy.model.database_utils import is_one_database
|
||||
from galaxy.model.database_utils import (
|
||||
database_exists,
|
||||
is_one_database,
|
||||
)
|
||||
from galaxy.model.migrations import (
|
||||
AlembicManager,
|
||||
DatabaseConfig,
|
||||
@@ -38,6 +41,51 @@ GXY_CONFIG_PREFIX = "GALAXY_CONFIG_"
|
||||
TSI_CONFIG_PREFIX = "GALAXY_INSTALL_CONFIG_"
|
||||
|
||||
|
||||
class DatabaseDoesNotExistError(Exception):
|
||||
def __init__(self, db_url: str) -> None:
|
||||
super().__init__(
|
||||
f"""The database at {db_url} does not exist. You must
|
||||
create and initialize the database before running this script. You
|
||||
can do so by (a) running `create_db.sh`; or by (b) starting Galaxy,
|
||||
in which case Galaxy will create and initialize the database
|
||||
automatically."""
|
||||
)
|
||||
|
||||
|
||||
class DatabaseNotInitializedError(Exception):
|
||||
def __init__(self, db_url: str) -> None:
|
||||
super().__init__(
|
||||
f"""The database at {db_url} is empty. You must
|
||||
initialize the database before running this script. You can do so by
|
||||
(a) running `create_db.sh`; or by (b) starting Galaxy, in which case
|
||||
Galaxy will initialize the database automatically."""
|
||||
)
|
||||
|
||||
|
||||
def verify_database_is_initialized(db_url: str) -> None:
|
||||
"""
|
||||
Intended for use by scripts that run database migrations (manage_db.sh,
|
||||
run_alembic.sh). Those scripts are meant to run on a database that has been
|
||||
initialized with the appropriate metadata (e.g. galaxy or install model).
|
||||
|
||||
This function will raise an error if the database does not exist or has not
|
||||
been initialized*.
|
||||
|
||||
*NOTE: this function cannot determine whether a database has been properly
|
||||
initialized; it can only tell when a database has *not* been initialized.
|
||||
"""
|
||||
if not database_exists(db_url):
|
||||
raise DatabaseDoesNotExistError(db_url)
|
||||
|
||||
engine = create_engine(db_url)
|
||||
try:
|
||||
db_state = DatabaseStateCache(engine=engine)
|
||||
if db_state.is_database_empty() or db_state.contains_only_kombu_tables():
|
||||
raise DatabaseNotInitializedError(db_url)
|
||||
finally:
|
||||
engine.dispose()
|
||||
|
||||
|
||||
def get_configuration(argv: List[str], cwd: str) -> Tuple[DatabaseConfig, DatabaseConfig, bool]:
|
||||
"""
|
||||
Return a 3-item-tuple with configuration values used for managing databases.
|
||||
@@ -120,7 +168,15 @@ class LegacyScripts:
|
||||
def __init__(self, argv: List[str], cwd: Optional[str] = None) -> None:
|
||||
self.argv = argv
|
||||
self.cwd = cwd or os.getcwd()
|
||||
self.database = self.DEFAULT_DB_ARG
|
||||
self._database: Optional[str] = None # Do not assign default value: `None` means we don't know yet.
|
||||
|
||||
@property
|
||||
def database(self):
|
||||
if self._database is None:
|
||||
raise LegacyScriptsException(
|
||||
"Attempt to access identifier of database before processing the script arguments"
|
||||
)
|
||||
return self._database
|
||||
|
||||
def run(self) -> None:
|
||||
"""
|
||||
@@ -147,8 +203,9 @@ class LegacyScripts:
|
||||
If last argument is a valid database name, pop and assign it; otherwise assign default.
|
||||
"""
|
||||
arg = self.argv[-1]
|
||||
self._database = self.DEFAULT_DB_ARG
|
||||
if arg in ["galaxy", "install"]:
|
||||
self.database = self.argv.pop()
|
||||
self._database = self.argv.pop()
|
||||
|
||||
def rename_config_argument(self) -> None:
|
||||
"""
|
||||
@@ -196,6 +253,12 @@ class LegacyScripts:
|
||||
elif self.database == "install":
|
||||
self.argv.append("tsi@head")
|
||||
|
||||
def get_db_url(self):
|
||||
if self.database in ["galaxy", self.DEFAULT_DB_ARG]:
|
||||
return self.gxy_url
|
||||
elif self.database == "install":
|
||||
return self.tsi_url
|
||||
|
||||
def _rename_arg(self, old_name, new_name) -> None:
|
||||
pos = self.argv.index(old_name)
|
||||
self.argv[pos] = new_name
|
||||
|
||||
@@ -3987,7 +3987,7 @@ exclusively use ``filter``s to populate options.
|
||||
* ``from_dataset`` - The options for the select list are dynamically obtained
|
||||
from input dataset selected for the tool from the current history.
|
||||
* ``from_file`` - The options for the select list are dynamically obtained from
|
||||
a file. This mechanis is discourage in favor of the more generic
|
||||
a file. This mechanism is discouraged in favor of the more generic
|
||||
``from_data_table``.
|
||||
* ``from_parameter`` - The options for the select list are dynamically obtained
|
||||
from a parameter.
|
||||
@@ -4063,6 +4063,10 @@ Starting from Galaxy v21.01, ``meta_file_key`` can be used together with
|
||||
medadata file that the ``meta_file_key`` implies, instead of the dataset
|
||||
itself.
|
||||
|
||||
Note that in any case only the first mega byte of the referred dataset (or file)
|
||||
is considered. Lines starting with ``#`` are ignored. By using the ``startswith``
|
||||
attribute also lines starting with other strings can be ignored.
|
||||
|
||||
```xml
|
||||
<param name="input" type="data" format="maf" label="MAF File"/>
|
||||
<param name="species" type="select" optional="False" label="Select species for the input dataset" multiple="True">
|
||||
@@ -4137,7 +4141,7 @@ used to generate dynamic options.
|
||||
</xs:sequence>
|
||||
<xs:attribute name="from_dataset" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Determine options from the dataset given in the referred input parameter. If 'meta_file_key' is given, the options are determined from the data in the metadata file of the input.</xs:documentation>
|
||||
<xs:documentation xml:lang="en">Determine options from (the first MB of) the dataset given in the referred input parameter. If 'meta_file_key' is given, the options are determined from (the first MB of) the data in the metadata file of the input.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="from_file" type="xs:string">
|
||||
@@ -4167,7 +4171,7 @@ used to generate dynamic options.
|
||||
</xs:attribute>
|
||||
<xs:attribute name="startswith" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Ignore lines starting with the given character.</xs:documentation>
|
||||
<xs:documentation xml:lang="en">Ignore lines starting with the given string.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="meta_file_key" type="xs:string">
|
||||
@@ -4175,6 +4179,11 @@ used to generate dynamic options.
|
||||
<xs:documentation xml:lang="en">Works with from_dataset only. See [docs](#from-dataset)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="separator" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Split tabular data with this character (default is tab)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:group name="OptionsElement">
|
||||
<xs:choice>
|
||||
|
||||
@@ -249,20 +249,20 @@ IMPLICITLY_REQUIRED_TOOL_FILES: Dict[str, Dict] = {
|
||||
# "foobar": {"required": REQUIRE_FULL_DIRECTORY}
|
||||
# if no version is specified, all versions without explicit RequiredFiles will be selected
|
||||
"circos": {"required": REQUIRE_FULL_DIRECTORY},
|
||||
"cp_image_math": {"required": {"includes": [{"path": "cp_common_functions.py", "path_type": "literal"}]}},
|
||||
"enumerate_charges": {"required": {"includes": [{"path": "site_substructures.smarts", "path_type": "literal"}]}},
|
||||
"cp_image_math": {"required": {"includes": [{"path": "*.py", "path_type": "glob"}]}},
|
||||
"enumerate_charges": {"required": REQUIRE_FULL_DIRECTORY},
|
||||
"fasta_compute_length": {"required": {"includes": [{"path": "utils/*", "path_type": "glob"}]}},
|
||||
"fasta_concatenate0": {"required": {"includes": [{"path": "utils/*", "path_type": "glob"}]}},
|
||||
"filter_tabular": {"required": {"includes": [{"path": "*.py", "path_type": "glob"}]}},
|
||||
"flanking_features_1": {"required": {"includes": [{"path": "utils/*", "path_type": "glob"}]}},
|
||||
"gops_intersect_1": {"required": {"includes": [{"path": "utils/*", "path_type": "glob"}]}},
|
||||
"gops_subtract_1": {"required": {"includes": [{"path": "utils/*", "path_type": "glob"}]}},
|
||||
"maxquant": {"required": {"includes": [{"path": "mqparam.py", "path_type": "literal"}]}},
|
||||
"maxquant_mqpar": {"required": {"includes": [{"path": "mqparam.py", "path_type": "literal"}]}},
|
||||
"maxquant": {"required": {"includes": [{"path": "*.py", "path_type": "glob"}]}},
|
||||
"maxquant_mqpar": {"required": {"includes": [{"path": "*.py", "path_type": "glob"}]}},
|
||||
"query_tabular": {"required": {"includes": [{"path": "*.py", "path_type": "glob"}]}},
|
||||
"shasta": {"required": {"includes": [{"path": "configs/*", "path_type": "glob"}]}},
|
||||
"sqlite_to_tabular": {"required": {"includes": [{"path": "*.py", "path_type": "glob"}]}},
|
||||
"sucos_max_score": {"required": {"includes": [{"path": "utils.py", "path_type": "literal"}]}},
|
||||
"sucos_max_score": {"required": {"includes": [{"path": "*.py", "path_type": "glob"}]}},
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -655,7 +655,7 @@ class DynamicOptions:
|
||||
rval = []
|
||||
field_count = None
|
||||
for line in reader:
|
||||
if line.startswith("#") or (self.line_startswith and not line.startswith(self.line_startswith)):
|
||||
if line.startswith("#") or (self.line_startswith and line.startswith(self.line_startswith)):
|
||||
continue
|
||||
line = line.rstrip("\n\r")
|
||||
if line:
|
||||
|
||||
+203
-142
@@ -1,16 +1,36 @@
|
||||
"""
|
||||
Module for building and searching the index of tools
|
||||
installed within this Galaxy. Before changing index-building
|
||||
or searching related parts it is deeply recommended to read
|
||||
through the library docs at https://whoosh.readthedocs.io.
|
||||
Module for building and searching the index of installed tools.
|
||||
|
||||
Before changing index-building or searching related parts it is highly
|
||||
recommended to read the docs at https://whoosh.readthedocs.io.
|
||||
|
||||
Schema - this is how we define the index, both for building and searching. A
|
||||
field is created for each data element that we want to add e.g. tool name,
|
||||
tool ID, description. The type of field and its attributes define how
|
||||
entries for that field will be indexed and ultimately how they can be
|
||||
searched. Score weighting (boost) is added here on a per-field bases, to
|
||||
allow matches to important fields like "name" to receive a higher score.
|
||||
|
||||
Tokenizers - these take an attribute (e.g. name) and parse it into "tokens" to
|
||||
be stored in the index. Can be done in many ways for different search
|
||||
functionality. For example, the IDTokenizer creates one token for an entire
|
||||
entry, resulting in an index field that requires a full-field match. The
|
||||
default tokenizer will break an entry into words, so that single word
|
||||
matches are possible.
|
||||
|
||||
Filters - various filters are available for processing content as the index is
|
||||
built. A StopFilter removes common articles 'a', 'for', 'and' etc. A
|
||||
StemmingFilter removes suffixes from words to create a 'base work' e.g.
|
||||
stemming -> stem; opened -> open; philosophy -> philosoph.
|
||||
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
from typing import (
|
||||
Dict,
|
||||
List,
|
||||
Tuple,
|
||||
Union,
|
||||
)
|
||||
|
||||
@@ -18,10 +38,10 @@ from whoosh import (
|
||||
analysis,
|
||||
index,
|
||||
)
|
||||
from whoosh.analysis import StandardAnalyzer
|
||||
from whoosh.fields import (
|
||||
ID,
|
||||
KEYWORD,
|
||||
NGRAMWORDS,
|
||||
Schema,
|
||||
TEXT,
|
||||
)
|
||||
@@ -31,10 +51,12 @@ from whoosh.qparser import (
|
||||
)
|
||||
from whoosh.scoring import (
|
||||
BM25F,
|
||||
Frequency,
|
||||
MultiWeighting,
|
||||
)
|
||||
from whoosh.writing import AsyncWriter
|
||||
|
||||
from galaxy.config import GalaxyAppConfiguration
|
||||
from galaxy.util import ExecutionTimer
|
||||
from galaxy.web.framework.helpers import to_unicode
|
||||
|
||||
@@ -44,16 +66,19 @@ CanConvertToFloat = Union[str, int, float]
|
||||
CanConvertToInt = Union[str, int, float]
|
||||
|
||||
|
||||
def get_or_create_index(index_dir: str, schema: Schema) -> index.Index:
|
||||
def get_or_create_index(index_dir, schema):
|
||||
"""Get or create a reference to the index."""
|
||||
if not os.path.exists(index_dir):
|
||||
os.makedirs(index_dir)
|
||||
if index.exists_in(index_dir):
|
||||
idx = index.open_dir(index_dir)
|
||||
try:
|
||||
assert idx.schema == schema
|
||||
if idx.schema == schema:
|
||||
return idx
|
||||
except AssertionError:
|
||||
log.warning("Index at '%s' uses outdated schema, creating new index", index_dir)
|
||||
log.warning(f"Index at '{index_dir}' uses outdated schema, creating a new index")
|
||||
|
||||
# Delete the old index and return a new index reference
|
||||
shutil.rmtree(index_dir)
|
||||
os.makedirs(index_dir)
|
||||
return index.create_in(index_dir, schema=schema)
|
||||
|
||||
|
||||
@@ -68,7 +93,12 @@ class ToolBoxSearch:
|
||||
for panel_view in toolbox.panel_views():
|
||||
panel_view_id = panel_view.id
|
||||
panel_index_dir = os.path.join(index_dir, panel_view_id)
|
||||
panel_searches[panel_view_id] = ToolPanelViewSearch(panel_view_id, panel_index_dir, index_help=index_help)
|
||||
panel_searches[panel_view_id] = ToolPanelViewSearch(
|
||||
panel_view_id,
|
||||
panel_index_dir,
|
||||
index_help=index_help,
|
||||
config=toolbox.app.config,
|
||||
)
|
||||
self.panel_searches = panel_searches
|
||||
# We keep track of how many times the tool index has been rebuilt.
|
||||
# We start at -1, so that after the first index the count is at 0,
|
||||
@@ -95,39 +125,111 @@ class ToolPanelViewSearch:
|
||||
the Whoosh search library.
|
||||
"""
|
||||
|
||||
def __init__(self, panel_view_id: str, index_dir: str, index_help: bool = True):
|
||||
self.schema = Schema(
|
||||
id=ID(stored=True, unique=True),
|
||||
old_id=ID,
|
||||
stub=KEYWORD,
|
||||
name=TEXT(analyzer=analysis.SimpleAnalyzer()),
|
||||
description=TEXT,
|
||||
section=TEXT,
|
||||
help=TEXT,
|
||||
labels=KEYWORD,
|
||||
)
|
||||
def __init__(
|
||||
self,
|
||||
panel_view_id: str,
|
||||
index_dir: str,
|
||||
config: GalaxyAppConfiguration,
|
||||
index_help: bool = True,
|
||||
):
|
||||
"""Build the schema and validate against the index."""
|
||||
schema_conf = {
|
||||
# The stored ID field is not searchable
|
||||
"id": ID(stored=True, unique=True),
|
||||
# This exact field is searchable by exact matches only
|
||||
"id_exact": TEXT(
|
||||
field_boost=(config.tool_id_boost * config.tool_name_exact_multiplier),
|
||||
analyzer=analysis.IDTokenizer() | analysis.LowercaseFilter(),
|
||||
),
|
||||
# The primary name field is searchable by exact match only, and is
|
||||
# eligible for massive score boosting. A secondary ngram or text
|
||||
# field for name is added below
|
||||
"name_exact": TEXT(
|
||||
field_boost=(config.tool_name_boost * config.tool_name_exact_multiplier),
|
||||
analyzer=analysis.IDTokenizer() | analysis.LowercaseFilter(),
|
||||
),
|
||||
# The owner/repo/tool_id parsed from the GUID
|
||||
"stub": KEYWORD(field_boost=float(config.tool_stub_boost)),
|
||||
# The section where the tool is listed in the tool panel
|
||||
"section": TEXT(field_boost=float(config.tool_section_boost)),
|
||||
# Short description defined in the tool XML
|
||||
"description": TEXT(
|
||||
field_boost=config.tool_description_boost,
|
||||
analyzer=analysis.StemmingAnalyzer(),
|
||||
),
|
||||
# Help text parsed from the tool XML
|
||||
"help": TEXT(field_boost=config.tool_help_boost, analyzer=analysis.StemmingAnalyzer()),
|
||||
"labels": KEYWORD(field_boost=float(config.tool_label_boost)),
|
||||
}
|
||||
|
||||
if config.tool_enable_ngram_search:
|
||||
schema_conf.update(
|
||||
{
|
||||
"name": NGRAMWORDS(
|
||||
minsize=config.tool_ngram_minsize,
|
||||
maxsize=config.tool_ngram_maxsize,
|
||||
field_boost=(float(config.tool_name_boost) * config.tool_ngram_factor),
|
||||
),
|
||||
}
|
||||
)
|
||||
else:
|
||||
schema_conf.update(
|
||||
{
|
||||
"name": TEXT(
|
||||
field_boost=float(config.tool_name_boost),
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
self.schema = Schema(**schema_conf)
|
||||
self.rex = analysis.RegexTokenizer()
|
||||
self.index_dir = index_dir
|
||||
self.panel_view_id = panel_view_id
|
||||
self.index = self._index_setup()
|
||||
|
||||
def _index_setup(self) -> index.Index:
|
||||
return get_or_create_index(index_dir=self.index_dir, schema=self.schema)
|
||||
"""Get or create a reference to the index."""
|
||||
return get_or_create_index(self.index_dir, self.schema)
|
||||
|
||||
def build_index(self, tool_cache, toolbox, index_help: bool = True) -> None:
|
||||
"""
|
||||
Prepare search index for tools loaded in toolbox.
|
||||
Use `tool_cache` to determine which tools need indexing and which tools should be expired.
|
||||
"""Prepare search index for tools loaded in toolbox.
|
||||
|
||||
Use `tool_cache` to determine which tools need indexing and which
|
||||
should be removed.
|
||||
"""
|
||||
log.debug(f"Starting to build toolbox index of panel {self.panel_view_id}.")
|
||||
execution_timer = ExecutionTimer()
|
||||
|
||||
with self.index.reader() as reader:
|
||||
# Index ocasionally contains empty stored fields
|
||||
indexed_tool_ids = {f["id"] for f in reader.all_stored_fields() if f}
|
||||
tool_ids_to_remove = (indexed_tool_ids - set(tool_cache._tool_paths_by_id.keys())).union(
|
||||
self.indexed_tool_ids = {f["id"] for f in reader.all_stored_fields() if f}
|
||||
|
||||
tool_ids_to_remove = self._get_tools_to_remove(tool_cache)
|
||||
tools_to_index = self._get_tool_list(
|
||||
toolbox,
|
||||
tool_cache,
|
||||
)
|
||||
|
||||
with AsyncWriter(self.index) as writer:
|
||||
for tool_id in tool_ids_to_remove:
|
||||
writer.delete_by_term("id", tool_id)
|
||||
for tool in tools_to_index:
|
||||
add_doc_kwds = self._create_doc(
|
||||
tool=tool,
|
||||
index_help=index_help,
|
||||
)
|
||||
# Add tool document to index (or overwrite if existing)
|
||||
writer.update_document(**add_doc_kwds)
|
||||
|
||||
log.debug(f"Toolbox index of panel {self.panel_view_id}" f" finished {execution_timer}")
|
||||
|
||||
def _get_tools_to_remove(self, tool_cache) -> list:
|
||||
"""Return list of tool IDs to be removed from index."""
|
||||
tool_ids_to_remove = (self.indexed_tool_ids - set(tool_cache._tool_paths_by_id.keys())).union(
|
||||
tool_cache._removed_tool_ids
|
||||
)
|
||||
for indexed_tool_id in indexed_tool_ids:
|
||||
|
||||
for indexed_tool_id in self.indexed_tool_ids:
|
||||
indexed_tool = tool_cache.get_tool_by_id(indexed_tool_id)
|
||||
if indexed_tool:
|
||||
if indexed_tool.is_latest_version:
|
||||
@@ -136,48 +238,58 @@ class ToolPanelViewSearch:
|
||||
if latest_version and latest_version.hidden:
|
||||
continue
|
||||
tool_ids_to_remove.add(indexed_tool_id)
|
||||
with AsyncWriter(self.index) as writer:
|
||||
for tool_id in tool_ids_to_remove:
|
||||
writer.delete_by_term("id", tool_id)
|
||||
for tool_id in tool_cache._new_tool_ids - indexed_tool_ids:
|
||||
tool = toolbox.get_tool(tool_id)
|
||||
if tool and tool.is_latest_version and toolbox.panel_has_tool(tool, self.panel_view_id):
|
||||
if tool.hidden:
|
||||
# we check if there is an older tool we can return
|
||||
if tool.lineage:
|
||||
for tool_version in reversed(tool.lineage.get_versions()):
|
||||
tool = tool_cache.get_tool_by_id(tool_version.id)
|
||||
if tool and not tool.hidden:
|
||||
tool_id = tool.id
|
||||
break
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
add_doc_kwds = self._create_doc(tool_id=tool_id, tool=tool, index_help=index_help)
|
||||
writer.update_document(**add_doc_kwds)
|
||||
log.debug(f"Toolbox index of panel {self.panel_view_id} finished {execution_timer}")
|
||||
|
||||
def _create_doc(self, tool_id: str, tool, index_help: bool = True) -> Dict[str, str]:
|
||||
# Do not add data managers to the public index
|
||||
return list(tool_ids_to_remove)
|
||||
|
||||
def _get_tool_list(self, toolbox, tool_cache) -> list:
|
||||
"""Return list of tools to add and remove from index."""
|
||||
tools_to_index = []
|
||||
|
||||
for tool_id in tool_cache._new_tool_ids - self.indexed_tool_ids:
|
||||
tool = toolbox.get_tool(tool_id)
|
||||
if tool and tool.is_latest_version and toolbox.panel_has_tool(tool, self.panel_view_id):
|
||||
if tool.hidden:
|
||||
# Check if there is an older tool we can return
|
||||
if tool.lineage:
|
||||
tool_versions = reversed(tool.lineage.get_versions())
|
||||
for tool_version in tool_versions:
|
||||
tool = tool_cache.get_tool_by_id(tool_version.id)
|
||||
if tool and not tool.hidden:
|
||||
break
|
||||
else:
|
||||
continue
|
||||
tools_to_index.append(tool)
|
||||
|
||||
return tools_to_index
|
||||
|
||||
def _create_doc(
|
||||
self,
|
||||
tool,
|
||||
index_help: bool = True,
|
||||
) -> Dict[str, str]:
|
||||
def clean(string):
|
||||
"""Remove hyphens as they are Whoosh wildcards."""
|
||||
if "-" in string:
|
||||
return (" ").join(token.text for token in self.rex(to_unicode(tool.name)))
|
||||
else:
|
||||
return string
|
||||
|
||||
if tool.tool_type == "manage_data":
|
||||
# Do not add data managers to the public index
|
||||
return {}
|
||||
add_doc_kwds = {
|
||||
"id": tool_id,
|
||||
"id": to_unicode(tool.id),
|
||||
"id_exact": to_unicode(tool.id),
|
||||
"name": clean(tool.name),
|
||||
"description": to_unicode(tool.description),
|
||||
"section": to_unicode(tool.get_panel_section()[1] if len(tool.get_panel_section()) == 2 else ""),
|
||||
"help": to_unicode(""),
|
||||
}
|
||||
if tool.name.find("-") != -1:
|
||||
# Replace hyphens, since they are wildcards in Whoosh causing false positives
|
||||
add_doc_kwds["name"] = (" ").join(token.text for token in self.rex(to_unicode(tool.name)))
|
||||
else:
|
||||
add_doc_kwds["name"] = to_unicode(tool.name)
|
||||
if tool.guid:
|
||||
# Create a stub consisting of owner, repo, and tool from guid
|
||||
slash_indexes = [m.start() for m in re.finditer("/", tool.guid)]
|
||||
id_stub = tool.guid[(slash_indexes[1] + 1) : slash_indexes[4]]
|
||||
add_doc_kwds["stub"] = (" ").join(token.text for token in self.rex(to_unicode(id_stub)))
|
||||
add_doc_kwds["stub"] = clean(id_stub)
|
||||
else:
|
||||
add_doc_kwds["stub"] = to_unicode(id)
|
||||
if tool.labels:
|
||||
@@ -188,100 +300,49 @@ class ToolPanelViewSearch:
|
||||
try:
|
||||
add_doc_kwds["help"] = to_unicode(raw_help)
|
||||
except Exception:
|
||||
# Don't fail to build index just because help can't be converted.
|
||||
# Don't fail to build index when help fails to parse
|
||||
pass
|
||||
|
||||
add_doc_kwds["name_exact"] = add_doc_kwds["name"]
|
||||
|
||||
return add_doc_kwds
|
||||
|
||||
def search(
|
||||
self,
|
||||
q: str,
|
||||
tool_name_boost: CanConvertToFloat,
|
||||
tool_id_boost: CanConvertToFloat,
|
||||
tool_section_boost: CanConvertToFloat,
|
||||
tool_description_boost: CanConvertToFloat,
|
||||
tool_label_boost: CanConvertToFloat,
|
||||
tool_stub_boost: CanConvertToFloat,
|
||||
tool_help_boost: CanConvertToFloat,
|
||||
tool_search_limit: CanConvertToFloat,
|
||||
tool_enable_ngram_search: bool,
|
||||
tool_ngram_minsize: CanConvertToInt,
|
||||
tool_ngram_maxsize: CanConvertToInt,
|
||||
config: GalaxyAppConfiguration,
|
||||
) -> List[str]:
|
||||
"""
|
||||
Perform search on the in-memory index. Weight in the given boosts.
|
||||
"""
|
||||
"""Perform search on the in-memory index."""
|
||||
# Change field boosts for searcher
|
||||
self.searcher = self.index.searcher(
|
||||
weighting=MultiWeighting(
|
||||
BM25F(),
|
||||
old_id=BM25F(old_id_B=float(tool_id_boost)),
|
||||
name=BM25F(name_B=float(tool_name_boost)),
|
||||
section=BM25F(section_B=float(tool_section_boost)),
|
||||
description=BM25F(description_B=float(tool_description_boost)),
|
||||
labels=BM25F(labels_B=float(tool_label_boost)),
|
||||
stub=BM25F(stub_B=float(tool_stub_boost)),
|
||||
help=BM25F(help_B=float(tool_help_boost)),
|
||||
Frequency(),
|
||||
help=BM25F(K1=config.tool_help_bm25f_k1),
|
||||
)
|
||||
)
|
||||
# Use OrGroup to change the default operation for joining multiple terms to logical OR.
|
||||
# This means e.g. for search 'bowtie of king arthur' a document that only has 'bowtie' will be a match.
|
||||
# https://whoosh.readthedocs.io/en/latest/api/qparser.html#whoosh.qparser.MultifieldPlugin
|
||||
# However this changes scoring i.e. searching 'bowtie of king arthur' a document with 'arthur arthur arthur'
|
||||
# would have a higher score than a document with 'bowtie arthur' which is usually unexpected for a user.
|
||||
# Hence we introduce a bonus on multi-hits using the 'factory()' method using a scaling factor between 0-1.
|
||||
# https://whoosh.readthedocs.io/en/latest/parsing.html#searching-for-any-terms-instead-of-all-terms-by-default
|
||||
# Adding the FuzzyTermPlugin to account for misspellings and typos, using a max distance of 2
|
||||
og = OrGroup.factory(0.9)
|
||||
fields = [
|
||||
"id",
|
||||
"id_exact",
|
||||
"name",
|
||||
"name_exact",
|
||||
"description",
|
||||
"section",
|
||||
"help",
|
||||
"labels",
|
||||
"stub",
|
||||
]
|
||||
self.parser = MultifieldParser(
|
||||
["name", "old_id", "description", "section", "help", "labels", "stub"], schema=self.schema, group=og
|
||||
fields,
|
||||
schema=self.schema,
|
||||
group=OrGroup, # We need OR grouping to match StopList phrases
|
||||
)
|
||||
cleaned_query = " ".join(token.text for token in self.rex(q.lower()))
|
||||
parsed_query = self.parser.parse(cleaned_query)
|
||||
hits = self.searcher.search(
|
||||
parsed_query,
|
||||
limit=float(config.tool_search_limit),
|
||||
sortedby="",
|
||||
terms=True,
|
||||
)
|
||||
|
||||
cleaned_query = q.lower()
|
||||
if tool_enable_ngram_search is True:
|
||||
rval = self._search_ngrams(cleaned_query, tool_ngram_minsize, tool_ngram_maxsize, tool_search_limit)
|
||||
return rval
|
||||
else:
|
||||
cleaned_query = " ".join(token.text for token in self.rex(cleaned_query))
|
||||
# Use asterisk Whoosh wildcard so e.g. 'bow' easily matches 'bowtie'
|
||||
parsed_query = self.parser.parse(f"*{cleaned_query}*")
|
||||
hits = self.searcher.search(parsed_query, limit=float(tool_search_limit), sortedby="")
|
||||
return [hit["id"] for hit in hits]
|
||||
|
||||
def _search_ngrams(
|
||||
self,
|
||||
cleaned_query: str,
|
||||
tool_ngram_minsize: CanConvertToInt,
|
||||
tool_ngram_maxsize: CanConvertToInt,
|
||||
tool_search_limit: CanConvertToFloat,
|
||||
) -> List[str]:
|
||||
"""
|
||||
Break tokens into ngrams and search on those instead.
|
||||
This should make searching more resistant to typos and unfinished words.
|
||||
See docs at https://whoosh.readthedocs.io/en/latest/ngrams.html
|
||||
"""
|
||||
hits_with_score: Dict[str, float] = {}
|
||||
token_analyzer = StandardAnalyzer() | analysis.NgramFilter(
|
||||
minsize=int(tool_ngram_minsize), maxsize=int(tool_ngram_maxsize)
|
||||
)
|
||||
ngrams = [token.text for token in token_analyzer(cleaned_query)]
|
||||
for query in ngrams:
|
||||
# Get the tool list with respective scores for each qgram
|
||||
curr_hits = self.searcher.search(self.parser.parse(f"*{query}*"), limit=float(tool_search_limit))
|
||||
for i, curr_hit in enumerate(curr_hits):
|
||||
is_present = False
|
||||
for prev_hit in hits_with_score:
|
||||
# Check if the tool appears again for the next qgram search
|
||||
if curr_hit["id"] == prev_hit:
|
||||
is_present = True
|
||||
# Add the current score with the previous one if the
|
||||
# tool appears again for the next qgram
|
||||
hits_with_score[prev_hit] = curr_hits.score(i) + hits_with_score[prev_hit]
|
||||
# Add the tool if not present to the collection with its score
|
||||
if not is_present:
|
||||
hits_with_score[curr_hit["id"]] = curr_hits.score(i)
|
||||
# Sort the results based on aggregated BM25 score in decreasing order of scores
|
||||
hits_with_score_list: List[Tuple[str, float]] = sorted(
|
||||
hits_with_score.items(), key=lambda x: x[1], reverse=True
|
||||
)
|
||||
# Return the tool ids
|
||||
return [item[0] for item in hits_with_score_list[0 : int(tool_search_limit)]]
|
||||
return [hit["id"] for hit in hits[: config.tool_search_limit]]
|
||||
|
||||
@@ -27,6 +27,7 @@ from starlette.responses import (
|
||||
)
|
||||
|
||||
from galaxy import util
|
||||
from galaxy.exceptions import RequestParameterInvalidException
|
||||
from galaxy.managers.context import ProvidesHistoryContext
|
||||
from galaxy.schema import (
|
||||
FilterQueryParams,
|
||||
@@ -252,6 +253,8 @@ def parse_dataset_details(details: Optional[str]):
|
||||
dataset_details: Optional[DatasetDetailsType] = None
|
||||
if details and details != "all":
|
||||
dataset_details = set(util.listify(details))
|
||||
if "" in dataset_details:
|
||||
raise RequestParameterInvalidException("Invalid empty IDs found in dataset details parameter")
|
||||
else: # either None or 'all'
|
||||
dataset_details = details # type: ignore
|
||||
return dataset_details
|
||||
|
||||
@@ -357,7 +357,9 @@ class LibraryDatasetsController(BaseGalaxyAPIController, UsesVisualizationMixin,
|
||||
trans.sa_session.flush()
|
||||
|
||||
rval = trans.security.encode_all_ids(library_dataset.to_dict())
|
||||
nice_size = util.nice_size(int(library_dataset.library_dataset_dataset_association.get_size()))
|
||||
nice_size = util.nice_size(
|
||||
int(library_dataset.library_dataset_dataset_association.get_size(calculate_size=False))
|
||||
)
|
||||
rval["file_size"] = nice_size
|
||||
rval["update_time"] = library_dataset.update_time.strftime("%Y-%m-%d %I:%M %p")
|
||||
rval["deleted"] = library_dataset.deleted
|
||||
|
||||
@@ -393,27 +393,34 @@ class UserAPIController(BaseGalaxyAPIController, UsesTagsMixin, BaseUIController
|
||||
email = user.email
|
||||
username = user.username
|
||||
inputs = list()
|
||||
inputs.append(
|
||||
{
|
||||
"id": "email_input",
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"label": "Email address",
|
||||
"value": email,
|
||||
"help": "If you change your email address you will receive an activation link in the new mailbox and you have to activate your account by visiting it.",
|
||||
}
|
||||
)
|
||||
if trans.webapp.name == "galaxy":
|
||||
user_info = {
|
||||
"email": email,
|
||||
"username": username,
|
||||
}
|
||||
is_galaxy_app = trans.webapp.name == "galaxy"
|
||||
if trans.app.config.enable_account_interface or not is_galaxy_app:
|
||||
inputs.append(
|
||||
{
|
||||
"id": "name_input",
|
||||
"name": "username",
|
||||
"id": "email_input",
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"label": "Public name",
|
||||
"value": username,
|
||||
"help": 'Your public name is an identifier that will be used to generate addresses for information you share publicly. Public names must be at least three characters in length and contain only lower-case letters, numbers, and the "-" character.',
|
||||
"label": "Email address",
|
||||
"value": email,
|
||||
"help": "If you change your email address you will receive an activation link in the new mailbox and you have to activate your account by visiting it.",
|
||||
}
|
||||
)
|
||||
if is_galaxy_app:
|
||||
if trans.app.config.enable_account_interface:
|
||||
inputs.append(
|
||||
{
|
||||
"id": "name_input",
|
||||
"name": "username",
|
||||
"type": "text",
|
||||
"label": "Public name",
|
||||
"value": username,
|
||||
"help": 'Your public name is an identifier that will be used to generate addresses for information you share publicly. Public names must be at least three characters in length and contain only lower-case letters, numbers, and the "-" character.',
|
||||
}
|
||||
)
|
||||
info_form_models = self.get_all_forms(
|
||||
trans, filter=dict(deleted=False), form_type=trans.app.model.FormDefinition.types.USER_INFO
|
||||
)
|
||||
@@ -441,25 +448,27 @@ class UserAPIController(BaseGalaxyAPIController, UsesTagsMixin, BaseUIController
|
||||
info_field["cases"].append({"value": info_form["id"], "inputs": info_form["inputs"]})
|
||||
inputs.append(info_field)
|
||||
|
||||
address_inputs = [{"type": "hidden", "name": "id", "hidden": True}]
|
||||
for field in AddressField.fields():
|
||||
address_inputs.append({"type": "text", "name": field[0], "label": field[1], "help": field[2]})
|
||||
address_repeat = {
|
||||
"title": "Address",
|
||||
"name": "address",
|
||||
"type": "repeat",
|
||||
"inputs": address_inputs,
|
||||
"cache": [],
|
||||
}
|
||||
address_values = [address.to_dict(trans) for address in user.addresses]
|
||||
for address in address_values:
|
||||
address_cache = []
|
||||
for input in address_inputs:
|
||||
input_copy = input.copy()
|
||||
input_copy["value"] = address.get(input["name"])
|
||||
address_cache.append(input_copy)
|
||||
address_repeat["cache"].append(address_cache)
|
||||
inputs.append(address_repeat)
|
||||
if trans.app.config.enable_account_interface:
|
||||
address_inputs = [{"type": "hidden", "name": "id", "hidden": True}]
|
||||
for field in AddressField.fields():
|
||||
address_inputs.append({"type": "text", "name": field[0], "label": field[1], "help": field[2]})
|
||||
address_repeat = {
|
||||
"title": "Address",
|
||||
"name": "address",
|
||||
"type": "repeat",
|
||||
"inputs": address_inputs,
|
||||
"cache": [],
|
||||
}
|
||||
address_values = [address.to_dict(trans) for address in user.addresses]
|
||||
for address in address_values:
|
||||
address_cache = []
|
||||
for input in address_inputs:
|
||||
input_copy = input.copy()
|
||||
input_copy["value"] = address.get(input["name"])
|
||||
address_cache.append(input_copy)
|
||||
address_repeat["cache"].append(address_cache)
|
||||
inputs.append(address_repeat)
|
||||
user_info["addresses"] = [address.to_dict(trans) for address in user.addresses]
|
||||
|
||||
# Build input sections for extra user preferences
|
||||
extra_user_pref = self._build_extra_user_pref_inputs(trans, self._get_extra_user_preferences(trans), user)
|
||||
@@ -488,12 +497,8 @@ class UserAPIController(BaseGalaxyAPIController, UsesTagsMixin, BaseUIController
|
||||
help='Your public name provides a means of identifying you publicly within this tool shed. Public names must be at least three characters in length and contain only lower-case letters, numbers, and the "-" character. You cannot change your public name after you have created a repository in this tool shed.',
|
||||
)
|
||||
)
|
||||
return {
|
||||
"email": email,
|
||||
"username": username,
|
||||
"addresses": [address.to_dict(trans) for address in user.addresses],
|
||||
"inputs": inputs,
|
||||
}
|
||||
user_info["inputs"] = inputs
|
||||
return user_info
|
||||
|
||||
@expose_api
|
||||
def set_information(self, trans, id, payload=None, **kwd):
|
||||
|
||||
@@ -244,32 +244,11 @@ class ToolsService(ServiceBase):
|
||||
:return type: dict
|
||||
"""
|
||||
panel_view = view or self.config.default_panel_view
|
||||
tool_name_boost = self.config.get("tool_name_boost", 9)
|
||||
tool_id_boost = self.config.get("tool_id_boost", 9)
|
||||
tool_section_boost = self.config.get("tool_section_boost", 3)
|
||||
tool_description_boost = self.config.get("tool_description_boost", 2)
|
||||
tool_label_boost = self.config.get("tool_label_boost", 1)
|
||||
tool_stub_boost = self.config.get("tool_stub_boost", 5)
|
||||
tool_help_boost = self.config.get("tool_help_boost", 0.5)
|
||||
tool_search_limit = self.config.get("tool_search_limit", 20)
|
||||
tool_enable_ngram_search = self.config.get("tool_enable_ngram_search", False)
|
||||
tool_ngram_minsize = self.config.get("tool_ngram_minsize", 3)
|
||||
tool_ngram_maxsize = self.config.get("tool_ngram_maxsize", 4)
|
||||
|
||||
results = self.toolbox_search.search(
|
||||
q=q,
|
||||
panel_view=panel_view,
|
||||
tool_name_boost=tool_name_boost,
|
||||
tool_id_boost=tool_id_boost,
|
||||
tool_section_boost=tool_section_boost,
|
||||
tool_description_boost=tool_description_boost,
|
||||
tool_label_boost=tool_label_boost,
|
||||
tool_stub_boost=tool_stub_boost,
|
||||
tool_help_boost=tool_help_boost,
|
||||
tool_search_limit=tool_search_limit,
|
||||
tool_enable_ngram_search=tool_enable_ngram_search,
|
||||
tool_ngram_minsize=tool_ngram_minsize,
|
||||
tool_ngram_maxsize=tool_ngram_maxsize,
|
||||
config=self.config,
|
||||
)
|
||||
return results
|
||||
|
||||
|
||||
@@ -164,6 +164,14 @@ class HistoryContentsApiTestCase(ApiTestCase):
|
||||
hda_details = self.__check_for_hda(contents_response, hda1)
|
||||
self.__assert_hda_has_full_details(hda_details)
|
||||
|
||||
def test_index_detail_parameter_error(self):
|
||||
hda1 = self.dataset_populator.new_dataset(self.history_id)
|
||||
wrong_details_query_with_empty_ids = f"details=,,{hda1['id']}"
|
||||
contents_response = self._get(
|
||||
f"histories/{self.history_id}/contents?v=dev&{wrong_details_query_with_empty_ids}"
|
||||
)
|
||||
self._assert_status_code_is(contents_response, 400)
|
||||
|
||||
def test_show_hda(self):
|
||||
hda1 = self.dataset_populator.new_dataset(self.history_id)
|
||||
show_response = self.__show(hda1)
|
||||
|
||||
@@ -55,6 +55,7 @@ console_scripts =
|
||||
[options.extras_require]
|
||||
cwl =
|
||||
cwltool==3.1.20211107152837
|
||||
schema-salad==8.3.20220525163636
|
||||
mulled =
|
||||
jinja2
|
||||
Whoosh
|
||||
|
||||
@@ -124,14 +124,9 @@ if [ $SET_VENV -eq 1 ] && [ $CREATE_VENV -eq 1 ]; then
|
||||
python3 -m venv "$GALAXY_VIRTUAL_ENV"
|
||||
else
|
||||
# If $GALAXY_VIRTUAL_ENV does not exist, and there is no conda available, attempt to create it.
|
||||
if [ -z "$GALAXY_PYTHON" ]; then
|
||||
if command -v python3 >/dev/null; then
|
||||
GALAXY_PYTHON=python3
|
||||
else
|
||||
GALAXY_PYTHON=python
|
||||
fi
|
||||
fi
|
||||
|
||||
# Ensure Python is a supported version before creating $GALAXY_VIRTUAL_ENV
|
||||
find_python_command
|
||||
"$GALAXY_PYTHON" ./scripts/check_python.py || exit 1
|
||||
echo "Creating Python virtual environment for Galaxy: $GALAXY_VIRTUAL_ENV"
|
||||
echo "using Python: $GALAXY_PYTHON"
|
||||
|
||||
@@ -90,6 +90,16 @@ conda_activate() {
|
||||
fi
|
||||
}
|
||||
|
||||
find_python_command() {
|
||||
if [ -z "$GALAXY_PYTHON" ]; then
|
||||
if command -v python3 >/dev/null; then
|
||||
GALAXY_PYTHON=python3
|
||||
else
|
||||
GALAXY_PYTHON=python
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
setup_python() {
|
||||
# If there is a .venv/ directory, assume it contains a virtualenv that we
|
||||
# should run this instance in.
|
||||
@@ -120,7 +130,8 @@ setup_python() {
|
||||
# in case you don't.
|
||||
[ -n "$PYTHONPATH" ] && echo 'WARNING: $PYTHONPATH is set, this can cause problems importing Galaxy dependencies'
|
||||
|
||||
python ./scripts/check_python.py || exit 1
|
||||
find_python_command
|
||||
"$GALAXY_PYTHON" ./scripts/check_python.py || exit 1
|
||||
}
|
||||
|
||||
setup_gravity_state_dir() {
|
||||
|
||||
@@ -25,12 +25,15 @@ sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pa
|
||||
from galaxy.model.migrations.scripts import (
|
||||
invoke_alembic,
|
||||
LegacyScripts,
|
||||
verify_database_is_initialized,
|
||||
)
|
||||
|
||||
|
||||
def run():
|
||||
ls = LegacyScripts(sys.argv, os.getcwd())
|
||||
ls.run()
|
||||
db_url = ls.get_db_url()
|
||||
verify_database_is_initialized(db_url)
|
||||
invoke_alembic()
|
||||
|
||||
|
||||
|
||||
@@ -1,19 +1,30 @@
|
||||
<tool id="options_from_metadata_file" name="Test for options from dataset metadata file" version="1.0.0" profile="21.01">
|
||||
<command><![CDATA[
|
||||
echo '${species}' > '${output}';
|
||||
echo '${species_comma}' >> '${output}';
|
||||
echo '${species_2}' >> '${output}'
|
||||
]]></command>
|
||||
<inputs>
|
||||
<param name="input" type="data" format="maf" label="MAF File"/>
|
||||
<!-- test meta_file_key to define options-->
|
||||
<param name="species" type="select" optional="false" label="Select species for the input dataset" multiple="true">
|
||||
<options from_dataset="input" meta_file_key="species_chromosomes" >
|
||||
<column name="name" index="0"/>
|
||||
<column name="value" index="0"/>
|
||||
<options from_dataset="input" meta_file_key="species_chromosomes">
|
||||
<column name="name" index="0"/>
|
||||
<column name="value" index="0"/>
|
||||
</options>
|
||||
</param>
|
||||
<param name="input_2" type="data_collection" collection_type="list" format="maf" label="MAF Collection" multiple="true" />
|
||||
<!-- test meta_file_key in combination with separator to define options
|
||||
(since the metadata file does not contain commas each line gets an option)-->
|
||||
<param name="species_comma" type="select" optional="false" label="Select species for the input dataset">
|
||||
<options from_dataset="input" meta_file_key="species_chromosomes" separator=",">
|
||||
<column name="name" index="0"/>
|
||||
<column name="value" index="0"/>
|
||||
</options>
|
||||
</param>
|
||||
<!-- test meta_file_key referring a collection to define options-->
|
||||
<param name="input_2" type="data_collection" collection_type="list" format="maf" label="MAF Collection" multiple="true"/>
|
||||
<param name="species_2" type="select" optional="false" label="Select species for the input dataset" multiple="true">
|
||||
<options from_dataset="input_2" meta_file_key="species_chromosomes" >
|
||||
<options from_dataset="input_2" meta_file_key="species_chromosomes">
|
||||
<column name="name" index="0"/>
|
||||
<column name="value" index="0"/>
|
||||
<filter type="unique_value" name="unique_param" column="0"/>
|
||||
@@ -21,23 +32,25 @@ echo '${species_2}' >> '${output}'
|
||||
</param>
|
||||
</inputs>
|
||||
<outputs>
|
||||
<data format="txt" name="output" />
|
||||
<data format="txt" name="output"/>
|
||||
</outputs>
|
||||
<tests>
|
||||
<test>
|
||||
<param name="input" value="3.maf" ftype="maf" />
|
||||
<param name="species" value="hg17,canFam1" />
|
||||
<param name="input" value="3.maf" ftype="maf"/>
|
||||
<param name="species" value="hg17,canFam1"/>
|
||||
<param name="species_comma" value="hg17	chr7"/>
|
||||
<param name="input_2">
|
||||
<collection type="list">
|
||||
<element name="e1" value="3.maf" />
|
||||
<element name="e2" value="4.maf" />
|
||||
<element name="e1" value="3.maf"/>
|
||||
<element name="e2" value="4.maf"/>
|
||||
</collection>
|
||||
</param>
|
||||
<param name="species_2" value="panTro1,rn3,bosTau2" />
|
||||
<param name="species_2" value="panTro1,rn3,bosTau2"/>
|
||||
<output name="output" ftype="txt">
|
||||
<assert_contents>
|
||||
<has_line line="hg17,canFam1" />
|
||||
<has_line line="panTro1,rn3,bosTau2" />
|
||||
<has_line line="hg17,canFam1"/>
|
||||
<has_line line="hg17__tc__chr7"/>
|
||||
<has_line line="panTro1,rn3,bosTau2"/>
|
||||
</assert_contents>
|
||||
</output>
|
||||
</test>
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
<tool file="filter_param_value_ref_attribute.xml" />
|
||||
<tool file="filter_static_regexp.xml" />
|
||||
<tool file="select_from_dataset.xml" />
|
||||
<tool file="select_from_csvdataset.xml" />
|
||||
<tool file="select_from_dataset_optional.xml" />
|
||||
<tool file="drill_down.xml" />
|
||||
<tool file="dbkey_filter_input.xml" />
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<tool id="select_from_csvdataset" name="select_from_csvdataset" version="0.1.0">
|
||||
<description>Create dynamic options from CSV data sets</description>
|
||||
<command><![CDATA[
|
||||
echo select_single '$select_single' > '$output'
|
||||
]]></command>
|
||||
<inputs>
|
||||
<param name="single" type="data" format="csv" label="single"/>
|
||||
<param name="select_single" type="select" label="select_single">
|
||||
<options from_dataset="single" separator="," startswith="Transaction_date">
|
||||
<column name="name" index="1"/>
|
||||
<column name="value" index="2"/>
|
||||
<validator type="no_options" message="No data is available in single" />
|
||||
</options>
|
||||
</param>
|
||||
</inputs>
|
||||
|
||||
<outputs>
|
||||
<data name="output" format="txt" />
|
||||
</outputs>
|
||||
|
||||
<tests>
|
||||
<!-- test that csv can be selected, i.e. if the separator attribute of <options> works-->
|
||||
<test>
|
||||
<param name="single" value="1.csv" />
|
||||
<param name="select_single" value="Product1" />
|
||||
<output name="output">
|
||||
<assert_contents>
|
||||
<has_text text="select_single 1200" />
|
||||
</assert_contents>
|
||||
</output>
|
||||
</test>
|
||||
<!-- test that data from "comment" lines can not be selected, i.e. if the startswith attribute of <options> works-->
|
||||
<test expect_failure="true">
|
||||
<param name="single" value="1.csv" />
|
||||
<param name="select_single" value="Product" />
|
||||
</test>
|
||||
</tests>
|
||||
<help>
|
||||
</help>
|
||||
</tool>
|
||||
@@ -1,8 +1,13 @@
|
||||
import random
|
||||
|
||||
import pytest
|
||||
|
||||
from galaxy.model.migrations.scripts import (
|
||||
DatabaseDoesNotExistError,
|
||||
DatabaseNotInitializedError,
|
||||
LegacyScripts,
|
||||
LegacyScriptsException,
|
||||
verify_database_is_initialized,
|
||||
)
|
||||
|
||||
|
||||
@@ -145,3 +150,25 @@ class TestLegacyScripts:
|
||||
argv = ["caller", "--alembic-config", "path-to-alembic", "downgrade"]
|
||||
with pytest.raises(LegacyScriptsException):
|
||||
LegacyScripts(argv).convert_args()
|
||||
|
||||
def test_access_database_id(self):
|
||||
db = "galaxy"
|
||||
argv = ["caller", "--alembic-config", "path-to-alembic", "upgrade", db]
|
||||
ls = LegacyScripts(argv)
|
||||
ls.run()
|
||||
assert ls.database == db
|
||||
|
||||
def test_access_database_id_before_processing_script_args_raises_error(self):
|
||||
argv = ["caller", "--alembic-config", "path-to-alembic", "upgrade"]
|
||||
with pytest.raises(LegacyScriptsException):
|
||||
LegacyScripts(argv).database
|
||||
|
||||
def test_verify_database_is_init_raises_error_if_no_database(self):
|
||||
nonexistant_path = str(random.random())[2:]
|
||||
db_url = f"sqlite:////{nonexistant_path}"
|
||||
with pytest.raises(DatabaseDoesNotExistError):
|
||||
verify_database_is_initialized(db_url)
|
||||
|
||||
def test_verify_database_is_init_raises_error_if_database_not_initialized(self, sqlite_memory_url):
|
||||
with pytest.raises(DatabaseNotInitializedError):
|
||||
verify_database_is_initialized(sqlite_memory_url)
|
||||
|
||||
Reference in New Issue
Block a user