Skip to content

Commit

Permalink
save for reuse for simulation assets (#4906)
Browse files Browse the repository at this point in the history
Co-authored-by: Cole Blanchard <[email protected]>
  • Loading branch information
blanchco and Cole Blanchard authored Sep 27, 2024
1 parent b8422fe commit 2aa5ac1
Show file tree
Hide file tree
Showing 10 changed files with 318 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
>
<template #value="slotProps">
<div class="dropdown-option">
<span>{{ getLabelById(slotProps.value) }}</span>
<span>{{ getOutputLabel(items, slotProps.value) }}</span>
<span class="timestamp"> {{ getCreateTimeById(slotProps.value) }} </span>
</div>
</template>
Expand All @@ -22,7 +22,7 @@
</template>
<template #option="slotProps">
<div class="dropdown-option">
<span>{{ slotProps.option?.label }}</span>
<span>{{ getOutputLabel(items, slotProps.option?.id) }}</span>
<span class="timestamp">
{{ getElapsedTimeText(slotProps.option?.timestamp) }}
</span>
Expand All @@ -39,6 +39,7 @@ import { computed } from 'vue';
import { WorkflowOutput, WorkflowPortStatus } from '@/types/workflow';
import Dropdown from 'primevue/dropdown';
import { getElapsedTimeText } from '@/utils/date';
import { getOutputLabel } from '@/services/workflow';

const props = defineProps<{
options: WorkflowOutput<any>[] | { label: string; items: WorkflowOutput<any>[] }[];
Expand All @@ -56,11 +57,6 @@ const getCreateTimeById = (id: string) => {
if (!option?.timestamp) return '';
return getElapsedTimeText(option.timestamp);
};

const getLabelById = (id: string) => {
const option = getOptionById(id);
return option?.label;
};
</script>

<style scoped>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<div class="port" />
</div>
<div class="relative w-full">
<div class="truncate text-right">{{ useProjects().getAssetName(output.value?.[0]) || output.label }}</div>
<div class="truncate text-right">{{ getOutputLabel(outputs, output.id) }}</div>
<Button
class="unlink"
label="Unlink"
Expand Down Expand Up @@ -72,9 +72,8 @@
import { PropType, computed, ref } from 'vue';
import { WorkflowPortStatus, WorkflowDirection, WorkflowOutput } from '@/types/workflow';
import Button from 'primevue/button';
import { OperatorMenuItem } from '@/services/workflow';
import { OperatorMenuItem, getOutputLabel } from '@/services/workflow';
import TeraOperatorMenu from '@/components/operator/tera-operator-menu.vue';
import { useProjects } from '@/composables/project';
const menuFocusId = ref<string | null>(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<template>
<tera-modal
v-if="isVisible"
class="w-4"
@modal-mask-clicked="closeModal"
@on-modal-open="newName = props.initialName ?? ''"
@modal-enter-press="save"
>
<template #header>
<h4>Save simulation assets</h4>
</template>
<template #default>
<form @submit.prevent>
<label for="new-name">What would you like to call it?</label>
<tera-input-text id="new-name" v-model="newName" placeholder="Enter a unique name" />
</form>
</template>
<template #footer>
<Button label="Save" size="large" @click="save" />
<Button label="Close" class="p-button-secondary" size="large" outlined @click="closeModal" />
</template>
</tera-modal>
</template>
<script setup lang="ts">
import TeraModal from '@/components/widgets/tera-modal.vue';
import Button from 'primevue/button';
import TeraInputText from '@/components/widgets/tera-input-text.vue';
import { AssetType } from '@/types/Types';
import { ref } from 'vue';
import { useProjects } from '@/composables/project';
import { createSimulationAssets } from '@/services/models/simulation-service';
const props = defineProps<{
initialName?: string;
isVisible: boolean;
simulationId: string;
assets: { id: string; type: AssetType }[];
}>();
const newName = ref('');
const emit = defineEmits(['close-modal', 'on-save']);
function save() {
createSimulationAssets(props.simulationId, newName.value, props.assets).then((data) => {
useProjects().refresh();
closeModal();
emit('on-save', data);
});
}
function closeModal() {
newName.value = '';
emit('close-modal');
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -374,14 +374,16 @@
</tera-slider-panel>
</template>
</tera-drilldown>
<tera-save-asset-modal
<tera-save-simulation-modal
:initial-name="configuredModelConfig?.name"
:is-visible="showSaveModal"
:asset="configuredModelConfig"
:asset-type="AssetType.ModelConfiguration"
:assets="[
{ id: configuredModelConfig?.id ?? '', type: AssetType.ModelConfiguration },
{ id: outputDatasetId, type: AssetType.Dataset }
]"
@close-modal="showSaveModal = false"
@on-save="onSaveAsModelConfiguration"
is-updating-asset
:simulation-id="node.state.forecastId"
/>
</template>
Expand Down Expand Up @@ -419,9 +421,9 @@ import {
CsvAsset,
DatasetColumn,
ModelConfiguration,
AssetType,
ChartAnnotation,
InterventionPolicy
InterventionPolicy,
AssetType
} from '@/types/Types';
import { CiemssPresetTypes, DrilldownTabs, ChartSetting, ChartSettingType } from '@/types/common';
import { getTimespan, drilldownChartSize, nodeMetadata } from '@/components/workflow/util';
Expand Down Expand Up @@ -452,7 +454,7 @@ import TeraChartControl from '@/components/workflow/tera-chart-control.vue';
import TeraInputText from '@/components/widgets/tera-input-text.vue';
import { displayNumber } from '@/utils/number';
import TeraPyciemssCancelButton from '@/components/pyciemss/tera-pyciemss-cancel-button.vue';
import TeraSaveAssetModal from '@/components/project/tera-save-asset-modal.vue';
import TeraSaveSimulationModal from '@/components/project/tera-save-simulation-modal.vue';
import { useClientEvent } from '@/composables/useClientEvent';
import { getInterventionPolicyById } from '@/services/intervention-policy';
import TeraInterventionSummaryCard from '@/components/workflow/ops/simulate-ciemss/tera-intervention-summary-card.vue';
Expand Down Expand Up @@ -496,6 +498,12 @@ const presetType = computed(() => {
return '';
});
const outputDatasetId = computed(() => {
if (!selectedOutputId.value) return '';
const output = props.node.outputs.find((o) => o.id === selectedOutputId.value);
return output?.value?.[0]?.datasetId ?? '';
});
const speedPreset = Object.freeze({
numSamples: 1,
method: CiemssMethodOptions.euler,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,14 +372,16 @@
@click="saveModelConfiguration"
/>
</Dialog>
<tera-save-asset-modal
<tera-save-simulation-modal
:initial-name="optimizedInterventionPolicy?.name"
:is-visible="showSaveInterventionPolicy"
:asset="optimizedInterventionPolicy"
:asset-type="AssetType.InterventionPolicy"
:assets="[
{ id: optimizedInterventionPolicy?.id ?? '', type: AssetType.InterventionPolicy },
{ id: datasetId, type: AssetType.Dataset }
]"
@close-modal="showSaveInterventionPolicy = false"
@on-save="onSaveForReuse"
is-updating-asset
:simulation-id="node.state.postForecastRunId"
/>
</template>
Expand All @@ -391,7 +393,7 @@ import Dropdown from 'primevue/dropdown';
import TeraInputText from '@/components/widgets/tera-input-text.vue';
import SelectButton from 'primevue/selectbutton';
import Dialog from 'primevue/dialog';
import TeraSaveAssetModal from '@/components/project/tera-save-asset-modal.vue';
import TeraSaveSimulationModal from '@/components/project/tera-save-simulation-modal.vue';
import TeraDatasetDatatable from '@/components/dataset/tera-dataset-datatable.vue';
import TeraDrilldown from '@/components/drilldown/tera-drilldown.vue';
import TeraDrilldownSection from '@/components/drilldown/tera-drilldown-section.vue';
Expand Down Expand Up @@ -525,6 +527,12 @@ const showSpinner = computed<boolean>(
() => props.node.state.inProgressOptimizeId !== '' || props.node.state.inProgressPostForecastId !== ''
);
const datasetId = computed(() => {
if (!selectedOutputId.value) return '';
const output = props.node.outputs.find((o) => o.id === selectedOutputId.value);
return output?.value?.[0]?.datasetId ?? '';
});
const showModelModal = ref(false);
const displayOptimizationResultMessage = ref(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,10 @@

<!-- Preview -->
<template #preview>
<tera-drilldown-preview
v-if="selectedOutputId"
title="Simulation output"
:options="outputs"
v-model:output="selectedOutputId"
@update:selection="onSelection"
:is-loading="showSpinner"
is-selectable
>
<tera-drilldown-section v-if="selectedOutputId" :is-loading="showSpinner">
<template #header-controls-right>
<Button label="Save for re-use" severity="secondary" outlined @click="showSaveDataset = true" />
</template>
<tera-operator-output-summary
v-if="node.state.summaryId && runResults[selectedRunId]"
:summary-id="node.state.summaryId"
Expand Down Expand Up @@ -186,7 +181,7 @@
/>
</div>
</template>
</tera-drilldown-preview>
</tera-drilldown-section>

<!-- Empty state -->
<section v-else class="empty-state">
Expand All @@ -195,6 +190,12 @@
</section>
</template>
</tera-drilldown>
<tera-save-simulation-modal
:is-visible="showSaveDataset"
@close-modal="showSaveDataset = false"
:simulation-id="node.state.forecastId"
:assets="[{ id: datasetId, type: AssetType.Dataset }]"
/>
</template>

<script setup lang="ts">
Expand All @@ -207,6 +208,7 @@ import EmptySeed from '@/assets/images/lottie-empty-seed.json';
import TeraInputNumber from '@/components/widgets/tera-input-number.vue';
import TeraSliderPanel from '@/components/widgets/tera-slider-panel.vue';
import type { CsvAsset, InterventionPolicy, SimulationRequest, TimeSpan } from '@/types/Types';
import { AssetType } from '@/types/Types';
import type { WorkflowNode } from '@/types/workflow';
import {
getRunResultCSV,
Expand All @@ -223,7 +225,6 @@ import TeraDatasetDatatable from '@/components/dataset/tera-dataset-datatable.vu
import SelectButton from 'primevue/selectbutton';
import TeraDrilldown from '@/components/drilldown/tera-drilldown.vue';
import TeraDrilldownSection from '@/components/drilldown/tera-drilldown-section.vue';
import TeraDrilldownPreview from '@/components/drilldown/tera-drilldown-preview.vue';
import TeraPyciemssCancelButton from '@/components/pyciemss/tera-pyciemss-cancel-button.vue';
import TeraNotebookError from '@/components/drilldown/tera-notebook-error.vue';
import TeraOperatorOutputSummary from '@/components/operator/tera-operator-output-summary.vue';
Expand All @@ -239,6 +240,7 @@ import { CiemssPresetTypes, DrilldownTabs } from '@/types/common';
import { getModelConfigurationById } from '@/services/model-configurations';
import { getInterventionPolicyById } from '@/services/intervention-policy';
import TeraInterventionSummaryCard from '@/components/workflow/ops/simulate-ciemss/tera-intervention-summary-card.vue';
import TeraSaveSimulationModal from '@/components/project/tera-save-simulation-modal.vue';
import { SimulateCiemssOperationState } from './simulate-ciemss-operation';
import TeraChartControl from '../../tera-chart-control.vue';
Expand All @@ -254,6 +256,11 @@ const codeText = ref('');
const policyInterventionId = computed(() => props.node.inputs[1].value?.[0]);
const interventionPolicy = ref<InterventionPolicy | null>(null);
const datasetId = computed(() => {
if (!selectedOutputId.value) return '';
const output = props.node.outputs.find((o) => o.id === selectedOutputId.value);
return output?.value?.[0] ?? '';
});
const llmThoughts = ref<any[]>([]);
const llmQuery = ref('');
Expand Down Expand Up @@ -302,18 +309,6 @@ let pyciemssMap: Record<string, string> = {};
const kernelManager = new KernelSessionManager();
const outputs = computed(() => {
if (!_.isEmpty(props.node.outputs)) {
return [
{
label: 'Select an output',
items: props.node.outputs
}
];
}
return [];
});
const presetType = computed(() => {
if (numSamples.value === speedPreset.numSamples && method.value === speedPreset.method) {
return CiemssPresetTypes.Fast;
Expand All @@ -332,6 +327,8 @@ const cancelRunId = computed(() => props.node.state.inProgressForecastId);
const outputPanel = ref(null);
const chartSize = computed(() => drilldownChartSize(outputPanel.value));
const showSaveDataset = ref(false);
const chartProxy = chartActionsProxy(props.node, (state: SimulateCiemssOperationState) => {
emit('update-state', state);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ import {
ProgressState,
Simulation,
SimulationRequest,
CsvAsset
CsvAsset,
TerariumAsset,
AssetType
} from '@/types/Types';
import { RunResults } from '@/types/SimulateConfig';
import * as EventService from '@/services/event';
import { useProjects } from '@/composables/project';
import { subscribe, unsubscribe } from '@/services/ClientEventService';
import { FIFOCache } from '@/utils/FifoCache';
import { AxiosResponse } from 'axios';

export type DataArray = Record<string, any>[];

Expand Down Expand Up @@ -245,6 +248,27 @@ export async function makeEnsembleCiemssCalibration(params: EnsembleCalibrationC
}
}

export async function createSimulationAssets(
simulationId: string,
newName: string | null,
assets: { id: string; type: AssetType }[]
) {
try {
const response: AxiosResponse<TerariumAsset[]> = await API.post(
`/simulations/${simulationId}/create-assets-from-simulation`,
{
name: newName,
assets
}
);
const output = response.data;
return output;
} catch (err) {
logger.error(err);
return null;
}
}

export async function subscribeToUpdateMessages(
simulationIds: string[],
eventType: ClientEventType,
Expand Down
15 changes: 15 additions & 0 deletions packages/client/hmi-client/src/services/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
WorkflowTransformations,
Transform
} from '@/types/workflow';
import { useProjects } from '@/composables/project';

/**
* A wrapper class around the workflow data struture to make it easier
Expand Down Expand Up @@ -676,6 +677,20 @@ export function getPortLabel({ label, type, isOptional }: WorkflowPort) {
return portLabel;
}

export function getOutputLabel(outputs: WorkflowOutput<any>[], id: string) {
const selectedOutput = outputs.find((output) => output.id === id);
if (!selectedOutput) return '';

// multiple output types, choose first name to use as label arbitrarily
if (selectedOutput.type.includes('|')) {
const outputType = selectedOutput.type.split('|');
return useProjects().getAssetName(selectedOutput.value?.[0]?.[outputType?.[0]]) || selectedOutput.label;
}

// default use single output type
return useProjects().getAssetName(selectedOutput.value?.[0]) || selectedOutput.label;
}

// Checker for resource-operators (e.g. model, dataset) that automatically create an output
// without needing to "run" the operator because we can drag them onto the canvas
export function canPropagateResource(outputs: WorkflowOutput<any>[]) {
Expand Down
Loading

0 comments on commit 2aa5ac1

Please sign in to comment.