mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-11-01 19:24:29 +08:00
Prep workflow label mapping for vitessce
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { BAlert } from "bootstrap-vue";
|
||||
import { computed, type Ref, ref, watch } from "vue";
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
import type { DatasetLabel, Invocation } from "@/components/Markdown/Editor/types";
|
||||
import { parseInput, parseOutput } from "@/components/Markdown/Utilities/parseInvocation";
|
||||
import { getAppRoot } from "@/onload";
|
||||
import { useInvocationStore } from "@/stores/invocationStore";
|
||||
|
||||
import LoadingSpan from "@/components/LoadingSpan.vue";
|
||||
@@ -13,23 +14,18 @@ const props = defineProps<{
|
||||
content: string;
|
||||
}>();
|
||||
|
||||
const datasetLabel: Ref<DatasetLabel | undefined> = ref();
|
||||
const errorMessage = ref("");
|
||||
const loading = ref(false);
|
||||
const visualizationConfig = ref();
|
||||
const visualizationKey = ref(0);
|
||||
const visualizationName = ref("");
|
||||
const visualizationTitle = ref("");
|
||||
|
||||
const { getInvocationById, getInvocationLoadError, isLoadingInvocation } = useInvocationStore();
|
||||
const { fetchInvocationById } = useInvocationStore();
|
||||
|
||||
const currentContent = ref(props.content);
|
||||
|
||||
const invocation = computed(() => invocationId.value && getInvocationById(invocationId.value));
|
||||
const invocationId = computed(() => datasetLabel.value?.invocation_id || "");
|
||||
const invocationLoading = computed(() => isLoadingInvocation(invocationId.value));
|
||||
const invocationLoadError = computed(() => getInvocationLoadError(invocationId.value));
|
||||
|
||||
function processContent() {
|
||||
async function processContent() {
|
||||
try {
|
||||
errorMessage.value = "";
|
||||
const parsedContent = { ...JSON.parse(props.content) };
|
||||
@@ -40,17 +36,29 @@ function processContent() {
|
||||
if (Array.isArray(dataset.files)) {
|
||||
for (const file of dataset.files) {
|
||||
if ("gxy_dataset_label" in file) {
|
||||
delete file.gxy_dataset_label;
|
||||
const gxyDatasetLabel = file.gxy_dataset_label;
|
||||
const invocationId = gxyDatasetLabel.invocation_id;
|
||||
const invocation = await fetchInvocationById(invocationId);
|
||||
const datasetId = getDatasetId(invocation as Invocation, gxyDatasetLabel);
|
||||
if (datasetId) {
|
||||
const datasetId = getDatasetId(invocation as Invocation, gxyDatasetLabel);
|
||||
file.url = `${getAppRoot()}api/datasets/${datasetId}/display`;
|
||||
delete file.gxy_dataset_label;
|
||||
} else {
|
||||
throw new Error(`Failed to retrieve dataset id from ${invocationId}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build visualization config for vitessce
|
||||
visualizationConfig.value = {};
|
||||
visualizationConfig.value["dataset_content"] = parsedContent;
|
||||
visualizationName.value = "vitessce";
|
||||
processInvocation();
|
||||
|
||||
// Trigger update counter
|
||||
if (currentContent.value !== props.content) {
|
||||
currentContent.value = props.content;
|
||||
visualizationKey.value++;
|
||||
@@ -60,13 +68,10 @@ function processContent() {
|
||||
}
|
||||
}
|
||||
|
||||
function processInvocation() {
|
||||
if (invocation.value) {
|
||||
const inputId = parseInput(invocation.value as Invocation, datasetLabel.value?.input);
|
||||
const outputId = parseOutput(invocation.value as Invocation, datasetLabel.value?.output);
|
||||
const datasetId = inputId || outputId;
|
||||
visualizationConfig.value.dataset_id = datasetId;
|
||||
}
|
||||
function getDatasetId(invocation: Invocation, datasetLabel: DatasetLabel) {
|
||||
const inputId = parseInput(invocation, datasetLabel?.input);
|
||||
const outputId = parseOutput(invocation, datasetLabel?.output);
|
||||
return inputId || outputId;
|
||||
}
|
||||
|
||||
watch(
|
||||
@@ -74,11 +79,6 @@ watch(
|
||||
() => processContent(),
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => invocation.value,
|
||||
() => processInvocation()
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -86,10 +86,7 @@ watch(
|
||||
<BAlert v-if="errorMessage" v-localize class="m-0" variant="danger" show>
|
||||
{{ errorMessage }}
|
||||
</BAlert>
|
||||
<BAlert v-else-if="invocationLoadError" v-localize class="m-0" variant="danger" show>
|
||||
{{ invocationLoadError }}
|
||||
</BAlert>
|
||||
<LoadingSpan v-else-if="invocationLoading" />
|
||||
<LoadingSpan v-else-if="loading" />
|
||||
<VisualizationWrapper
|
||||
v-else-if="visualizationConfig"
|
||||
:key="visualizationKey"
|
||||
|
||||
@@ -56,7 +56,7 @@ const invocationJobsSummaryById = {
|
||||
// Mock the invocation store to return the expected invocation data given the invocation ID
|
||||
jest.mock("@/stores/invocationStore", () => {
|
||||
const originalModule = jest.requireActual("@/stores/invocationStore");
|
||||
const mockFetchInvocationForId = jest.fn().mockImplementation((fetchParams) => {
|
||||
const mockFetchInvocationById = jest.fn().mockImplementation((fetchParams) => {
|
||||
if (fetchParams.id === "error-invocation") {
|
||||
throw new Error("User does not own specified item.");
|
||||
}
|
||||
@@ -72,10 +72,10 @@ jest.mock("@/stores/invocationStore", () => {
|
||||
getInvocationJobsSummaryById: jest.fn().mockImplementation((invocationId) => {
|
||||
return invocationJobsSummaryById[invocationId];
|
||||
}),
|
||||
fetchInvocationForId: mockFetchInvocationForId,
|
||||
fetchInvocationById: mockFetchInvocationById,
|
||||
fetchInvocationJobsSummaryForId: mockFetchInvocationJobsSummaryForId,
|
||||
}),
|
||||
mockFetchInvocationForId,
|
||||
mockFetchInvocationById,
|
||||
mockFetchInvocationJobsSummaryForId,
|
||||
};
|
||||
});
|
||||
@@ -207,8 +207,8 @@ function isInvocationAndJobTerminal(wrapper: Wrapper<Vue>): boolean {
|
||||
|
||||
/** Asserts that the invocation was fetched in the store the given number of times */
|
||||
function assertInvocationFetched(count = 1) {
|
||||
const { mockFetchInvocationForId } = jest.requireMock("@/stores/invocationStore");
|
||||
expect(mockFetchInvocationForId).toHaveBeenCalledTimes(count);
|
||||
const { mockFetchInvocationById } = jest.requireMock("@/stores/invocationStore");
|
||||
expect(mockFetchInvocationById).toHaveBeenCalledTimes(count);
|
||||
}
|
||||
|
||||
/** Asserts that the jobs summary was fetched in the store the given number of times */
|
||||
|
||||
@@ -183,7 +183,7 @@ watch(
|
||||
async (id) => {
|
||||
invocationLoaded.value = false;
|
||||
try {
|
||||
await invocationStore.fetchInvocationForId({ id });
|
||||
await invocationStore.fetchInvocationById({ id });
|
||||
invocationLoaded.value = true;
|
||||
// Only start polling if there is a valid invocation
|
||||
if (invocation.value) {
|
||||
@@ -216,7 +216,7 @@ onUnmounted(() => {
|
||||
|
||||
async function pollStepStatesUntilTerminal() {
|
||||
if (!invocationSchedulingTerminal.value) {
|
||||
await invocationStore.fetchInvocationForId({ id: props.invocationId });
|
||||
await invocationStore.fetchInvocationById({ id: props.invocationId });
|
||||
stepStatesInterval.value = setTimeout(pollStepStatesUntilTerminal, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export const useInvocationStore = defineStore("invocationStore", () => {
|
||||
}
|
||||
|
||||
const {
|
||||
fetchItemById: fetchInvocationForId,
|
||||
fetchItemById: fetchInvocationById,
|
||||
getItemById: getInvocationById,
|
||||
getItemLoadError: getInvocationLoadError,
|
||||
isLoadingItem: isLoadingInvocation,
|
||||
@@ -71,7 +71,7 @@ export const useInvocationStore = defineStore("invocationStore", () => {
|
||||
|
||||
return {
|
||||
cancelWorkflowScheduling,
|
||||
fetchInvocationForId,
|
||||
fetchInvocationById,
|
||||
fetchInvocationJobsSummaryForId,
|
||||
fetchInvocationStepById,
|
||||
getInvocationById,
|
||||
|
||||
Reference in New Issue
Block a user