Prep workflow label mapping for vitessce

This commit is contained in:
guerler
2025-04-03 14:15:03 -04:00
parent feeeb15e0d
commit df778be360
4 changed files with 33 additions and 36 deletions
@@ -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);
}
}
+2 -2
View File
@@ -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,