mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge pull request #17571 from itisAliRH/collections-common-refactors
Collections common refactors
This commit is contained in:
@@ -61,3 +61,9 @@ export const fetchCollectionAttributes = fetcher
|
||||
.path("/api/dataset_collections/{id}/attributes")
|
||||
.method("get")
|
||||
.create();
|
||||
|
||||
const postCopyCollection = fetcher.path("/api/dataset_collections/{id}/copy").method("post").create();
|
||||
export async function copyCollection(id: string, dbkey: string): Promise<Record<string, never>> {
|
||||
const { data } = await postCopyCollection({ id, dbkey });
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -8,3 +8,7 @@ export const undeleteHistory = fetcher.path("/api/histories/deleted/{history_id}
|
||||
export const undeleteHistories = fetcher.path("/api/histories/batch/undelete").method("put").create();
|
||||
export const publishedHistoriesFetcher = fetcher.path("/api/histories/published").method("get").create();
|
||||
export const historyFetcher = fetcher.path("/api/histories/{history_id}").method("get").create();
|
||||
export const updateHistoryItemsInBulk = fetcher
|
||||
.path("/api/histories/{history_id}/contents/bulk")
|
||||
.method("put")
|
||||
.create();
|
||||
|
||||
@@ -1,14 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import Multiselect from "vue-multiselect";
|
||||
|
||||
import localize from "@/utils/localization";
|
||||
|
||||
interface Props {
|
||||
// TODO: Replace with actual datatype type
|
||||
datatypes: any[];
|
||||
datatypeFromElements: string;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
// TODO: Replace with actual datatype type
|
||||
(e: "clicked-save", datatype: any): void;
|
||||
}>();
|
||||
|
||||
const currentDatatype = ref(props.datatypeFromElements);
|
||||
const selectedDatatype = ref(props.datatypes.find((element) => element.id == props.datatypeFromElements));
|
||||
|
||||
const hasSelectedDatatype = computed(() => {
|
||||
return Boolean(selectedDatatype.value);
|
||||
});
|
||||
const enableSave = computed(() => {
|
||||
return hasSelectedDatatype.value && selectedDatatype.value.id != currentDatatype.value;
|
||||
});
|
||||
|
||||
function clickedSave() {
|
||||
emit("clicked-save", selectedDatatype.value);
|
||||
|
||||
currentDatatype.value = selectedDatatype.value.id;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<span class="float-left h-sm">Change Datatype/Extension of all elements in collection</span>
|
||||
|
||||
<div class="text-right">
|
||||
<button class="save-datatype-edit btn btn-primary" :disabled="!enableSave" @click="clickedSave">
|
||||
{{ l("Save") }}
|
||||
{{ localize("Save") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<b>{{ l("New Type") }}: </b>
|
||||
|
||||
<b>{{ localize("New Type") }}: </b>
|
||||
|
||||
<Multiselect
|
||||
v-if="hasSelectedDatatype"
|
||||
v-model="selectedDatatype"
|
||||
@@ -23,44 +62,3 @@
|
||||
</Multiselect>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Multiselect from "vue-multiselect";
|
||||
|
||||
export default {
|
||||
components: { Multiselect },
|
||||
props: {
|
||||
datatypes: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
datatypeFromElements: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
selectedDatatype: {},
|
||||
currentDatatype: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hasSelectedDatatype() {
|
||||
return Boolean(this.selectedDatatype);
|
||||
},
|
||||
enableSave() {
|
||||
return this.hasSelectedDatatype && this.selectedDatatype.id != this.currentDatatype;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.selectedDatatype = this.datatypes.find((element) => element.id == this.datatypeFromElements);
|
||||
this.currentDatatype = this.datatypeFromElements;
|
||||
},
|
||||
methods: {
|
||||
clickedSave: function () {
|
||||
this.$emit("clicked-save", this.selectedDatatype);
|
||||
this.currentDatatype = this.selectedDatatype.id;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,3 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "input", value: string): void;
|
||||
}>();
|
||||
|
||||
const editable = ref(false);
|
||||
const localValue = ref(props.value);
|
||||
|
||||
watch(
|
||||
() => localValue.value,
|
||||
(newLocalValue) => {
|
||||
emit("input", newLocalValue);
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newValue) => {
|
||||
localValue.value = newValue;
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<input
|
||||
v-if="editable"
|
||||
@@ -6,41 +38,13 @@
|
||||
contenteditable
|
||||
@blur="editable = false"
|
||||
@keyup.enter="editable = false" />
|
||||
|
||||
<label v-else @click="editable = true">
|
||||
{{ localValue }}
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
title: {
|
||||
required: false,
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
editable: false,
|
||||
localValue: this.value,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
localValue(newValue) {
|
||||
this.$emit("input", newValue);
|
||||
},
|
||||
value(newValue) {
|
||||
this.localValue = newValue;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style scoped lang="scss">
|
||||
.click-to-edit-input {
|
||||
width: 600px;
|
||||
line-height: 1 !important;
|
||||
|
||||
@@ -1,3 +1,55 @@
|
||||
<script setup lang="ts">
|
||||
import { library } from "@fortawesome/fontawesome-svg-core";
|
||||
import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import { computed, ref, watch } from "vue";
|
||||
|
||||
import localize from "@/utils/localization";
|
||||
|
||||
library.add(faChevronDown, faChevronUp);
|
||||
|
||||
interface Props {
|
||||
oncancel: () => void;
|
||||
hideSourceItems: boolean;
|
||||
suggestedName?: string;
|
||||
renderExtensionsToggle?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
suggestedName: "",
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "remove-extensions-toggle"): void;
|
||||
(e: "clicked-create", value: string): void;
|
||||
(e: "onUpdateHideSourceItems", value: boolean): void;
|
||||
}>();
|
||||
|
||||
const isExpanded = ref(false);
|
||||
const collectionName = ref(props.suggestedName);
|
||||
const localHideSourceItems = ref(props.hideSourceItems);
|
||||
|
||||
const validInput = computed(() => {
|
||||
return collectionName.value.length > 0;
|
||||
});
|
||||
|
||||
function clickForHelp() {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
return isExpanded.value;
|
||||
}
|
||||
|
||||
function cancelCreate() {
|
||||
props.oncancel();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => localHideSourceItems.value,
|
||||
() => {
|
||||
emit("onUpdateHideSourceItems", localHideSourceItems.value);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="collection-creator">
|
||||
<div class="header flex-row no-flex">
|
||||
@@ -6,69 +58,78 @@
|
||||
class="more-help"
|
||||
href="javascript:void(0);"
|
||||
role="button"
|
||||
:title="titleForHelp"
|
||||
@click="_clickForHelp">
|
||||
:title="localize('Expand or Close Help')"
|
||||
@click="clickForHelp">
|
||||
<div v-if="!isExpanded">
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
<FontAwesomeIcon :icon="faChevronDown" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<i class="fas fa-chevron-up"></i>
|
||||
<FontAwesomeIcon :icon="faChevronUp" />
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="help-content">
|
||||
<!-- each collection that extends this will add their own help content -->
|
||||
<slot name="help-content"></slot>
|
||||
|
||||
<a
|
||||
class="more-help"
|
||||
href="javascript:void(0);"
|
||||
role="button"
|
||||
:title="titleForHelp"
|
||||
@click="_clickForHelp">
|
||||
:title="localize('Expand or Close Help')"
|
||||
@click="clickForHelp">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="middle flex-row flex-row-container">
|
||||
<slot name="middle-content"></slot>
|
||||
</div>
|
||||
|
||||
<div class="footer flex-row no-flex">
|
||||
<div class="attributes clear">
|
||||
<div class="clear">
|
||||
<label v-if="renderExtensionsToggle" class="setting-prompt float-right">
|
||||
{{ removeFileExtensionsText }}
|
||||
{{ localize("Remove file extensions?") }}
|
||||
<input
|
||||
class="remove-extensions float-right"
|
||||
type="checkbox"
|
||||
checked
|
||||
@click="$emit('remove-extensions-toggle')" />
|
||||
@click="emit('remove-extensions-toggle')" />
|
||||
</label>
|
||||
|
||||
<label class="setting-prompt float-right">
|
||||
{{ hideOriginalsText }}
|
||||
{{ localize("Hide original elements?") }}
|
||||
<input v-model="localHideSourceItems" class="hide-originals float-right" type="checkbox" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="clear">
|
||||
<input
|
||||
v-model="collectionName"
|
||||
class="collection-name form-control float-right"
|
||||
:placeholder="placeholderEnterName" />
|
||||
:placeholder="localize('Enter a name for your new collection')" />
|
||||
|
||||
<div class="collection-name-prompt float-right">
|
||||
{{ l("Name:") }}
|
||||
{{ localize("Name:") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions clear vertically-spaced">
|
||||
<div class="float-left">
|
||||
<button class="cancel-create btn" tabindex="-1" @click="_cancelCreate">
|
||||
{{ l("Cancel") }}
|
||||
<button class="cancel-create btn" tabindex="-1" @click="cancelCreate">
|
||||
{{ localize("Cancel") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="main-options float-right">
|
||||
<button
|
||||
class="create-collection btn btn-primary"
|
||||
:disabled="!validInput"
|
||||
@click="$emit('clicked-create', collectionName)">
|
||||
{{ l("Create collection") }}
|
||||
@click="emit('clicked-create', collectionName)">
|
||||
{{ localize("Create collection") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -76,71 +137,6 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _l from "utils/localization";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
oncancel: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
renderExtensionsToggle: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
hideSourceItems: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
suggestedName: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
titleForHelp: _l("Expand or Close Help"),
|
||||
hideOriginalsText: _l("Hide original elements?"),
|
||||
titleMoreHelp: _l("Close and show more help"),
|
||||
placeholderEnterName: _l("Enter a name for your new collection"),
|
||||
dropdownText: _l("Create a <i>single</> pair"),
|
||||
isExpanded: false,
|
||||
collectionName: this.suggestedName,
|
||||
removeFileExtensionsText: "Remove file extensions?",
|
||||
localHideSourceItems: this.hideSourceItems,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
validInput: function () {
|
||||
return this.collectionName.length > 0;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
localHideSourceItems() {
|
||||
this.$emit("onUpdateHideSourceItems", this.localHideSourceItems);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
l(str) {
|
||||
// _l conflicts private methods of Vue internals, expose as l instead
|
||||
return _l(str);
|
||||
},
|
||||
_clickForHelp: function () {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
return this.isExpanded;
|
||||
},
|
||||
_cancelCreate: function () {
|
||||
this.oncancel();
|
||||
},
|
||||
_getName: function () {
|
||||
return this.collectionName;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
$fa-font-path: "../../../../node_modules/@fortawesome/fontawesome-free/webfonts/";
|
||||
@import "~@fortawesome/fontawesome-free/scss/_variables";
|
||||
|
||||
@@ -1,21 +1,155 @@
|
||||
<script setup lang="ts">
|
||||
import { library } from "@fortawesome/fontawesome-svg-core";
|
||||
import { faBars, faCog, faDatabase, faTable, faUser } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import axios from "axios";
|
||||
import { BAlert, BSpinner, BTab, BTabs } from "bootstrap-vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, ref } from "vue";
|
||||
|
||||
import { copyCollection } from "@/api/datasetCollections";
|
||||
import { DatatypesProvider, DbKeyProvider, SuitableConvertersProvider } from "@/components/providers";
|
||||
import { useConfig } from "@/composables/config";
|
||||
import { useCollectionAttributesStore } from "@/stores/collectionAttributesStore";
|
||||
import { useHistoryStore } from "@/stores/historyStore";
|
||||
import localize from "@/utils/localization";
|
||||
import { prependPath } from "@/utils/redirect";
|
||||
import { errorMessageAsString } from "@/utils/simple-error";
|
||||
|
||||
import { HistoryContentBulkOperationPayload, updateHistoryItemsBulk } from "./services";
|
||||
|
||||
import ChangeDatatypeTab from "@/components/Collections/common/ChangeDatatypeTab.vue";
|
||||
import DatabaseEditTab from "@/components/Collections/common/DatabaseEditTab.vue";
|
||||
import SuitableConvertersTab from "@/components/Collections/common/SuitableConvertersTab.vue";
|
||||
import LoadingSpan from "@/components/LoadingSpan.vue";
|
||||
|
||||
library.add(faBars, faCog, faDatabase, faTable, faUser);
|
||||
|
||||
interface Props {
|
||||
collectionId: string;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { config, isConfigLoaded } = useConfig(true);
|
||||
const collectionAttributesStore = useCollectionAttributesStore();
|
||||
|
||||
const historyStore = useHistoryStore();
|
||||
const { currentHistoryId } = storeToRefs(historyStore);
|
||||
|
||||
const jobError = ref(null);
|
||||
const errorMessage = ref("");
|
||||
const infoMessage = ref("This will create a new collection in your History. Your quota will not increase.");
|
||||
|
||||
const attributesData = computed(() => {
|
||||
return collectionAttributesStore.getAttributes(props.collectionId);
|
||||
});
|
||||
const databaseKeyFromElements = computed(() => {
|
||||
return attributesData.value?.dbkey;
|
||||
});
|
||||
const datatypeFromElements = computed(() => {
|
||||
return attributesData.value?.extension;
|
||||
});
|
||||
|
||||
function updateInfoMessage(strMessage: string) {
|
||||
infoMessage.value = strMessage;
|
||||
}
|
||||
|
||||
// TODO: Replace with actual datatype type
|
||||
async function clickedSave(attribute: string, newValue: any) {
|
||||
if (attribute !== "dbkey") {
|
||||
// TODO: extend this to other attributes that could be changed
|
||||
console.error(`Changing ${attribute} not implemented`);
|
||||
return;
|
||||
}
|
||||
|
||||
const dbKey = newValue.id;
|
||||
|
||||
try {
|
||||
await copyCollection(props.collectionId, dbKey);
|
||||
} catch (err) {
|
||||
errorMessage.value = errorMessageAsString(err, "History import failed.");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace with actual datatype type
|
||||
async function clickedConvert(selectedConverter: any) {
|
||||
const url = prependPath(`/api/tools/${selectedConverter.tool_id}/convert`);
|
||||
const data = {
|
||||
src: "hdca",
|
||||
id: props.collectionId,
|
||||
source_type: selectedConverter.original_type,
|
||||
target_type: selectedConverter.target_type,
|
||||
};
|
||||
|
||||
try {
|
||||
await axios.post(url, data).catch(handleError);
|
||||
} catch (err) {
|
||||
errorMessage.value = errorMessageAsString(err, "History import failed.");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace with actual datatype type
|
||||
async function clickedDatatypeChange(selectedDatatype: any) {
|
||||
const data: HistoryContentBulkOperationPayload = {
|
||||
items: [
|
||||
{
|
||||
history_content_type: "dataset_collection",
|
||||
id: props.collectionId,
|
||||
},
|
||||
],
|
||||
operation: "change_datatype",
|
||||
params: {
|
||||
type: "change_datatype",
|
||||
datatype: selectedDatatype.id,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
await updateHistoryItemsBulk(currentHistoryId.value ?? "", data);
|
||||
} catch (err) {
|
||||
errorMessage.value = errorMessageAsString(err, "History import failed.");
|
||||
}
|
||||
}
|
||||
|
||||
function handleError(err: any) {
|
||||
errorMessage.value = errorMessageAsString(err, "History import failed.");
|
||||
|
||||
if (err?.data?.stderr) {
|
||||
jobError.value = err.data;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div aria-labelledby="collection-edit-view-heading">
|
||||
<h1 id="collection-edit-view-heading" class="h-lg">{{ l("Edit Collection Attributes") }}</h1>
|
||||
<b-alert show variant="info" dismissible>
|
||||
{{ l(infoMessage) }}
|
||||
</b-alert>
|
||||
<div v-if="jobError">
|
||||
<b-alert show variant="danger" dismissible>
|
||||
{{ l(errorMessage) }}
|
||||
</b-alert>
|
||||
</div>
|
||||
<b-tabs content-class="mt-3">
|
||||
<b-tab
|
||||
<h1 id="collection-edit-view-heading" class="h-lg">{{ localize("Edit Collection Attributes") }}</h1>
|
||||
|
||||
<BAlert show variant="info" dismissible>
|
||||
{{ localize(infoMessage) }}
|
||||
</BAlert>
|
||||
|
||||
<BAlert v-if="jobError" show variant="danger" dismissible>
|
||||
{{ localize(errorMessage) }}
|
||||
</BAlert>
|
||||
|
||||
<BTabs content-class="mt-3">
|
||||
<BTab
|
||||
title-link-class="collection-edit-change-genome-nav"
|
||||
@click="updateInfoMessage(newCollectionMessage + ' ' + noQuotaIncreaseMessage)">
|
||||
<template v-slot:title> <FontAwesomeIcon icon="table" /> {{ l("Database/Build") }}</template>
|
||||
@click="
|
||||
updateInfoMessage(
|
||||
'This will create a new collection in your History. Your quota will not increase.'
|
||||
)
|
||||
">
|
||||
<template v-slot:title>
|
||||
<FontAwesomeIcon :icon="faTable" />
|
||||
{{ localize("Database/Build") }}
|
||||
</template>
|
||||
|
||||
<DbKeyProvider v-slot="{ item, loading }">
|
||||
<div v-if="loading"><b-spinner label="Loading Database/Builds..."></b-spinner></div>
|
||||
<div v-if="loading">
|
||||
<BSpinner label="Loading Database/Builds..." />
|
||||
</div>
|
||||
<div v-else>
|
||||
<DatabaseEditTab
|
||||
v-if="item && databaseKeyFromElements"
|
||||
@@ -24,23 +158,39 @@
|
||||
@clicked-save="clickedSave" />
|
||||
</div>
|
||||
</DbKeyProvider>
|
||||
</b-tab>
|
||||
</BTab>
|
||||
|
||||
<SuitableConvertersProvider :id="collectionId" v-slot="{ item }">
|
||||
<b-tab
|
||||
<BTab
|
||||
v-if="item && item.length"
|
||||
title-link-class="collection-edit-convert-datatype-nav"
|
||||
@click="updateInfoMessage(newCollectionMessage)">
|
||||
<template v-slot:title> <FontAwesomeIcon icon="cog" /> {{ l("Convert") }}</template>
|
||||
@click="updateInfoMessage('This will create a new collection in your History.')">
|
||||
<template v-slot:title>
|
||||
<FontAwesomeIcon :icon="faCog" />
|
||||
{{ localize("Convert") }}
|
||||
</template>
|
||||
|
||||
<SuitableConvertersTab :suitable-converters="item" @clicked-convert="clickedConvert" />
|
||||
</b-tab>
|
||||
</BTab>
|
||||
</SuitableConvertersProvider>
|
||||
<b-tab
|
||||
|
||||
<BTab
|
||||
v-if="isConfigLoaded && config.enable_celery_tasks"
|
||||
title-link-class="collection-edit-change-datatype-nav"
|
||||
@click="updateInfoMessage(expectWaitTimeMessage)">
|
||||
<template v-slot:title> <FontAwesomeIcon icon="database" /> {{ l("Datatypes") }} </template>
|
||||
@click="
|
||||
updateInfoMessage(
|
||||
'This operation might take a short while, depending on the size of your collection.'
|
||||
)
|
||||
">
|
||||
<template v-slot:title>
|
||||
<FontAwesomeIcon :icon="faDatabase" />
|
||||
{{ localize("Datatypes") }}
|
||||
</template>
|
||||
|
||||
<DatatypesProvider v-slot="{ item, loading }">
|
||||
<div v-if="loading"><LoadingSpan :message="loadingString" /></div>
|
||||
<div v-if="loading">
|
||||
<LoadingSpan message="Loading Datatypes" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<ChangeDatatypeTab
|
||||
v-if="item && datatypeFromElements"
|
||||
@@ -49,131 +199,7 @@
|
||||
@clicked-save="clickedDatatypeChange" />
|
||||
</div>
|
||||
</DatatypesProvider>
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
</BTab>
|
||||
</BTabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { library } from "@fortawesome/fontawesome-svg-core";
|
||||
import { faBars, faCog, faDatabase, faTable, faUser } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import axios from "axios";
|
||||
import BootstrapVue from "bootstrap-vue";
|
||||
import { mapState } from "pinia";
|
||||
import { prependPath } from "utils/redirect";
|
||||
import { errorMessageAsString } from "utils/simple-error";
|
||||
import Vue from "vue";
|
||||
|
||||
import { useConfig } from "@/composables/config";
|
||||
import { useCollectionAttributesStore } from "@/stores/collectionAttributesStore";
|
||||
import { useHistoryStore } from "@/stores/historyStore";
|
||||
|
||||
import { DatatypesProvider, DbKeyProvider, SuitableConvertersProvider } from "../../providers";
|
||||
import ChangeDatatypeTab from "./ChangeDatatypeTab";
|
||||
import DatabaseEditTab from "./DatabaseEditTab";
|
||||
import SuitableConvertersTab from "./SuitableConvertersTab";
|
||||
|
||||
import LoadingSpan from "@/components/LoadingSpan.vue";
|
||||
|
||||
library.add(faDatabase, faTable, faBars, faUser, faCog);
|
||||
|
||||
Vue.use(BootstrapVue);
|
||||
export default {
|
||||
components: {
|
||||
DatabaseEditTab,
|
||||
SuitableConvertersTab,
|
||||
FontAwesomeIcon,
|
||||
DbKeyProvider,
|
||||
SuitableConvertersProvider,
|
||||
ChangeDatatypeTab,
|
||||
DatatypesProvider,
|
||||
LoadingSpan,
|
||||
},
|
||||
props: {
|
||||
collectionId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { config, isConfigLoaded } = useConfig(true);
|
||||
return { config, isConfigLoaded };
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
errorMessage: null,
|
||||
jobError: null,
|
||||
noQuotaIncrease: true,
|
||||
loadingString: "Loading Datatypes",
|
||||
infoMessage: "This will create a new collection in your History. Your quota will not increase.", //initialmessage on first/database tab
|
||||
newCollectionMessage: "This will create a new collection in your History.",
|
||||
noQuotaIncreaseMessage: "Your quota will not increase.",
|
||||
expectWaitTimeMessage: "This operation might take a short while, depending on the size of your collection.",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(useHistoryStore, ["currentHistoryId"]),
|
||||
...mapState(useCollectionAttributesStore, ["getAttributes"]),
|
||||
attributesData() {
|
||||
return this.getAttributes(this.collectionId);
|
||||
},
|
||||
databaseKeyFromElements: function () {
|
||||
return this.attributesData?.dbkey;
|
||||
},
|
||||
datatypeFromElements: function () {
|
||||
return this.attributesData?.extension;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateInfoMessage: function (strMessage) {
|
||||
this.infoMessage = strMessage;
|
||||
},
|
||||
clickedSave: function (attribute, newValue) {
|
||||
const url = prependPath(`/api/dataset_collections/${this.collectionId}/copy`);
|
||||
const data = {};
|
||||
if (attribute == "dbkey") {
|
||||
data["dbkey"] = newValue.id;
|
||||
} else {
|
||||
// TODO: extend this to other attributes that could be changed
|
||||
console.error(`Changing ${attribute} not implemented`);
|
||||
return;
|
||||
}
|
||||
axios.post(url, data).catch(this.handleError);
|
||||
},
|
||||
clickedConvert: function (selectedConverter) {
|
||||
const url = prependPath(`/api/tools/${selectedConverter.tool_id}/convert`);
|
||||
const data = {
|
||||
src: "hdca",
|
||||
id: this.collectionId,
|
||||
source_type: selectedConverter.original_type,
|
||||
target_type: selectedConverter.target_type,
|
||||
};
|
||||
axios.post(url, data).catch(this.handleError);
|
||||
},
|
||||
clickedDatatypeChange: function (selectedDatatype) {
|
||||
const url = prependPath(`/api/histories/${this.currentHistoryId}/contents/bulk`);
|
||||
const data = {
|
||||
operation: "change_datatype",
|
||||
items: [
|
||||
{
|
||||
history_content_type: "dataset_collection",
|
||||
id: this.collectionId,
|
||||
},
|
||||
],
|
||||
params: {
|
||||
type: "change_datatype",
|
||||
datatype: selectedDatatype.id,
|
||||
},
|
||||
};
|
||||
axios.put(url, data).catch(this.handleError);
|
||||
},
|
||||
handleError: function (err) {
|
||||
this.errorMessage = errorMessageAsString(err, "History import failed.");
|
||||
if (err?.data?.stderr) {
|
||||
this.jobError = err.data;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,17 +1,48 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import Multiselect from "vue-multiselect";
|
||||
|
||||
import localize from "@/utils/localization";
|
||||
|
||||
interface Props {
|
||||
genomes: { id: string; text: string }[];
|
||||
databaseKeyFromElements: string;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: "clicked-save", attribute: string, newValue: any): void;
|
||||
}>();
|
||||
|
||||
const selectedGenome = ref();
|
||||
|
||||
function clickedSave() {
|
||||
emit("clicked-save", "dbkey", selectedGenome.value);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
selectedGenome.value = props.genomes.find((element) => element.id == props.databaseKeyFromElements);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<span class="float-left h-sm">Change Database/Build of all elements in collection</span>
|
||||
|
||||
<div class="text-right">
|
||||
<button
|
||||
class="save-dbkey-edit btn btn-primary"
|
||||
:disabled="selectedGenome.id == databaseKeyFromElements"
|
||||
:disabled="selectedGenome?.id == databaseKeyFromElements"
|
||||
@click="clickedSave">
|
||||
{{ l("Save") }}
|
||||
{{ localize("Save") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<b>{{ l("Database/Build") }}: </b>
|
||||
|
||||
<b>{{ localize("Database/Build") }}: </b>
|
||||
|
||||
<Multiselect
|
||||
v-model="selectedGenome"
|
||||
class="database-dropdown"
|
||||
@@ -21,38 +52,7 @@
|
||||
:options="genomes"
|
||||
:searchable="true"
|
||||
:allow-empty="false">
|
||||
{{ selectedGenome.text }}
|
||||
{{ selectedGenome?.text }}
|
||||
</Multiselect>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Multiselect from "vue-multiselect";
|
||||
|
||||
export default {
|
||||
components: { Multiselect },
|
||||
props: {
|
||||
genomes: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
databaseKeyFromElements: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
selectedGenome: {},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.selectedGenome = this.genomes.find((element) => element.id == this.databaseKeyFromElements);
|
||||
},
|
||||
methods: {
|
||||
clickedSave: function () {
|
||||
this.$emit("clicked-save", "dbkey", this.selectedGenome);
|
||||
this.selectedGenome = this.genomes.find((element) => element.id == this.databaseKeyFromElements);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { updateHistoryItemsInBulk } from "@/api/histories";
|
||||
import { type components } from "@/api/schema";
|
||||
|
||||
export type HistoryContentBulkOperationPayload = components["schemas"]["HistoryContentBulkOperationPayload"];
|
||||
|
||||
export async function updateHistoryItemsBulk(historyId: string, changes: HistoryContentBulkOperationPayload) {
|
||||
await updateHistoryItemsInBulk({ history_id: historyId, ...changes });
|
||||
}
|
||||
Reference in New Issue
Block a user