Add dataset collection element picker

The placement could improvement but something's better than nothing.
This commit is contained in:
mvdbeek
2025-06-22 16:17:57 +02:00
committed by Nate Coraor
parent 627f04f13e
commit fa23d38e8e
5 changed files with 95 additions and 5 deletions
@@ -0,0 +1,58 @@
<script setup lang="ts">
import { computed, ref, watch } from "vue";
import { fetchCollectionElements, fetchCollectionSummary } from "@/api/datasetCollections";
interface CollectionElementPickerProps {
hdcaId: string;
}
const props = defineProps<CollectionElementPickerProps>();
const dceToId = ref<{
[k: string]: string | undefined;
}>();
const selectedValue = ref<string>();
watch(
() => props.hdcaId,
async () => {
const collectionSummary = await fetchCollectionSummary({ hdca_id: props.hdcaId });
const collectionElements = await fetchCollectionElements({
hdcaId: props.hdcaId,
collectionId: collectionSummary.collection_id,
limit: 10,
});
const identifierAndIds = Object.fromEntries(
collectionElements.map((element) => {
return [element.object?.id, element.element_identifier];
})
);
dceToId.value = identifierAndIds;
},
{ immediate: true }
);
const value = computed(() => selectedValue.value || Object.keys(dceToId.value || {}).at(0));
function handleInput(value: string) {
selectedValue.value = value;
}
</script>
<template>
<div>
<b-navbar class="align-items-center">
<b-collapse id="nav-text-collapse" is-nav>
<b-navbar-nav>
<b-nav-text class="mr-3">Select Element</b-nav-text>
</b-navbar-nav>
<b-nav-form>
<b-form-select
class="form-control-sm"
:value="value"
:options="dceToId"
@input="handleInput"></b-form-select>
</b-nav-form>
</b-collapse>
</b-navbar>
<slot name="element" :element="value"></slot>
</div>
</template>
@@ -14,6 +14,7 @@ import { useConfig } from "@/composables/config";
import { useInvocationStore } from "@/stores/invocationStore";
import { useWorkflowStore } from "@/stores/workflowStore";
import DatasetCollectionElementPicker from "./Elements/DatasetCollectionElementPicker.vue";
import HistoryDatasetAsImage from "./Elements/HistoryDatasetAsImage.vue";
import HistoryDatasetAsTable from "./Elements/HistoryDatasetAsTable.vue";
import HistoryDatasetCollectionDisplay from "./Elements/HistoryDatasetCollection/CollectionDisplay.vue";
@@ -240,10 +241,22 @@ watch(
:implicit-collection-jobs-id="args.implicit_collection_jobs_id"
:name="name" />
<VisualizationWrapper
v-else-if="name == 'visualization'"
v-else-if="name == 'visualization' && !args.history_dataset_collection_id"
:name="args.visualization_id"
:config="{ dataset_id: args.history_dataset_id }"
:height="args.height && parseInt(args.height)" />
<DatasetCollectionElementPicker
v-else-if="name == 'visualization'"
:hdca-id="args.history_dataset_collection_id">
<template v-slot:element="{ element }">
<VisualizationWrapper
v-if="element"
:key="element"
:name="args.visualization_id"
:config="{ dataset_id: element }"
:height="args.height && parseInt(args.height)" />
</template>
</DatasetCollectionElementPicker>
<WorkflowDisplay
v-else-if="name == 'workflow_display'"
:workflow-id="args.workflow_id"
@@ -21,6 +21,8 @@ const INVOCATION = {
output_1: {
id: "output_id_1",
},
},
output_collections: {
output_2: {
id: "output_id_2",
},
@@ -27,9 +27,16 @@ export function parseInput(invocation: Invocation, name: string | undefined) {
export function parseOutput(invocation: Invocation, name: string | undefined) {
if (!name) {
return undefined
return undefined;
}
return invocation.outputs[name]?.id || invocation.output_collections[name]?.id
return invocation.outputs[name]?.id;
}
export function parseOutputCollection(invocation: Invocation, name: string | undefined) {
if (!name) {
return undefined;
}
return invocation.output_collections[name]?.id;
}
export function parseStep(invocation: Invocation, name: string | undefined) {
@@ -53,6 +60,7 @@ export function parseInvocation(
const result: ParsedAttributes = { ...attributes, invocation };
const inputId = parseInput(invocation, result.input);
const outputId = parseOutput(invocation, result.output);
const outputCollectionId = parseOutputCollection(invocation, result.output);
const step = parseStep(invocation, result.step);
const requiredObject = getRequiredObject(name);
if (name === "history_link") {
@@ -65,8 +73,12 @@ export function parseInvocation(
result.history_dataset_collection_id = inputId;
} else if (outputId && "history_dataset_id" === requiredObject) {
result.history_dataset_id = outputId;
} else if (outputId && "history_dataset_collection_id" === requiredObject) {
result.history_dataset_collection_id = outputId;
} else if (outputCollectionId && "history_dataset_id" === requiredObject) {
// asked for a history_dataset_id but it's a collection, this can always happen
// because we map over the workflow
result.history_dataset_collection_id = outputCollectionId;
} else if (outputCollectionId && "history_dataset_collection_id" === requiredObject) {
result.history_dataset_collection_id = outputCollectionId;
} else if (step) {
result.job_id = step.job_id;
result.implicit_collection_jobs_id = step.implicit_collection_jobs_id;
@@ -61,5 +61,10 @@ export function hasValidName(name: string | undefined) {
export function hasValidObject(name: string | undefined, args: Record<string, string>): boolean {
const requiredObject = getRequiredObject(name);
if (requiredObject === "history_dataset_id" && args.history_dataset_collection_id) {
// accept a collection where a dataset is expected.
// we don't control this anyway
return true;
}
return !requiredObject || !!args[requiredObject];
}