Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

5965 task compare datasets node thumbnail should be one of the charts #6082

Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f6c6448
show charts on compare datsets node
mloppie Jan 15, 2025
3af6a69
add hide in node property to chart settings
mloppie Jan 15, 2025
32dcb98
hide charts from node in compare dataset node
mloppie Jan 15, 2025
62541d9
move compare datasets init to util; code clean up
mloppie Jan 15, 2025
0a684b7
reuse initialize util in drilldown; code cleanup
mloppie Jan 15, 2025
827c701
allow for hiding charts in simulate node
mloppie Jan 15, 2025
4fec0e0
allow hide chart in node in calibrate
mloppie Jan 15, 2025
b38a9e8
allow hiding charts in calibrate ensemble node
mloppie Jan 15, 2025
e78102f
lint
mloppie Jan 15, 2025
634530a
remove little dots
mloppie Jan 15, 2025
a1a41e9
Merge branch 'main' into 5965-task-compare-datasets-node-thumbnail-sh…
mloppie Jan 15, 2025
708c4ce
onChange fixes
mloppie Jan 15, 2025
0445c7e
reinitialize when state updates
mloppie Jan 15, 2025
57040af
Merge branch 'main' into 5965-task-compare-datasets-node-thumbnail-sh…
mloppie Jan 15, 2025
d45a974
Merge branch 'main' into 5965-task-compare-datasets-node-thumbnail-sh…
mloppie Jan 15, 2025
05e0e01
lint
mloppie Jan 15, 2025
a3b58f6
Update packages/client/hmi-client/src/components/workflow/ops/node-ch…
mloppie Jan 16, 2025
570993e
pr feedback
mloppie Jan 16, 2025
137aa5e
pr feedback
mloppie Jan 16, 2025
0dfa4e6
Merge branch 'main' into 5965-task-compare-datasets-node-thumbnail-sh…
mloppie Jan 16, 2025
6e291b6
make all node charts not interactive
mloppie Jan 16, 2025
a62046b
Merge branch '5965-task-compare-datasets-node-thumbnail-should-be-one…
mloppie Jan 16, 2025
834b438
Merge branch 'main' into 5965-task-compare-datasets-node-thumbnail-sh…
mloppie Jan 16, 2025
ee92c64
allow for groups of charts in node preview
mloppie Jan 16, 2025
69aec03
Update packages/client/hmi-client/src/components/workflow/ops/calibra…
mloppie Jan 16, 2025
0d3b15f
Merge branch 'main' into 5965-task-compare-datasets-node-thumbnail-sh…
mloppie Jan 16, 2025
d014026
code cleanup
mloppie Jan 16, 2025
29bb6d3
Merge branch '5965-task-compare-datasets-node-thumbnail-should-be-one…
mloppie Jan 16, 2025
2218147
revert drilldown change; code clean up
mloppie Jan 16, 2025
fdf16b0
Merge branch 'main' into 5965-task-compare-datasets-node-thumbnail-sh…
mloppie Jan 16, 2025
537d3a1
improve typing and templating
mloppie Jan 17, 2025
c9c621d
Merge branch '5965-task-compare-datasets-node-thumbnail-should-be-one…
mloppie Jan 17, 2025
ab6953b
Merge branch 'main' into 5965-task-compare-datasets-node-thumbnail-sh…
mloppie Jan 17, 2025
d26487a
Resolved merge conflict
jryu01 Jan 17, 2025
6543c09
Merge branch 'main' into 5965-task-compare-datasets-node-thumbnail-sh…
mloppie Jan 17, 2025
ff1d959
Merge branch '5965-task-compare-datasets-node-thumbnail-should-be-one…
mloppie Jan 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@
<div class="content">
<div class="annotation-items">
<h5>Options</h5>
<tera-checkbox label="Use log scale" :model-value="useLog" @update:model-value="toggleLogScale($event)" />
<tera-checkbox
label="Use log scale"
:model-value="Boolean(useLog)"
@update:model-value="toggleLogScale($event)"
label="Hide in node"
:model-value="isHiddenInNode"
@update:model-value="toggleHideInNode($event)"
/>
<tera-checkbox
v-if="comparison"
label="Small multiples"
:model-value="Boolean(isSmallMultiples)"
:model-value="isSmallMultiples"
@update:model-value="toggleSmallMultiples($event)"
/>
<!-- TODO: we want this but it is under research for how to get it to work in vega-lite -->
Expand Down Expand Up @@ -92,8 +93,9 @@ const props = defineProps<{
const emit = defineEmits(['close', 'update-settings', 'delete-annotation', 'create-annotation']);

// Log scale
const useLog = computed(() => props.activeSettings?.scale === 'log');
const isSmallMultiples = computed(() => {
const useLog = computed<boolean>(() => props.activeSettings?.scale === 'log');
const isHiddenInNode = computed<boolean>(() => !!props.activeSettings?.hideInNode);
const isSmallMultiples = computed<boolean>(() => {
const { smallMultiples, selectedVariables } = <ChartSetting>props.activeSettings;
return smallMultiples || selectedVariables?.length > 5;
});
Expand All @@ -103,6 +105,10 @@ const toggleLogScale = (useLogScale: boolean) => {
emit('update-settings', { scale: useLogScale ? 'log' : '' });
};

const toggleHideInNode = (hideInNode: boolean) => {
emit('update-settings', { hideInNode: !!hideInNode });
};

const toggleSmallMultiples = (smallMultiples: boolean) => {
emit('update-settings', { smallMultiples: !!smallMultiples });
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,22 @@
<template
v-if="!inProgressCalibrationId && runResult && csvAsset && runResultPre && selectedVariableSettings.length"
>
<vega-chart
v-for="setting of selectedVariableSettings"
:key="setting.id"
:are-embed-actions-visible="false"
:visualization-spec="variableCharts[setting.id]"
:interactive="false"
<tera-node-charts
:node="node"
:is-loading="!!inProgressCalibrationId"
:prepared-charts="variableCharts"
:chart-settings="selectedVariableSettings"
:progress="node.state.currentProgress + '%'"
/>
<vega-chart
v-for="setting of selectedInterventionSettings"
:key="setting.id"
expandable
:are-embed-actions-visible="true"
:visualization-spec="interventionCharts[setting.id]"
:interactive="false"
<tera-node-charts
:node="node"
:prepared-charts="interventionCharts"
:chart-settings="selectedInterventionSettings"
:expandable="true"
/>
</template>
<vega-chart v-else-if="lossChartSpec" :are-embed-actions-visible="false" :visualization-spec="lossChartSpec" />

<tera-progress-spinner v-if="inProgressCalibrationId" :font-size="2" is-centered style="height: 100%">
{{ node.state.currentProgress }}%
</tera-progress-spinner>

<Button v-if="areInputsFilled" label="Open" @click="emit('open-drilldown')" severity="secondary" outlined />
<tera-operator-placeholder v-else :node="node">
Connect a model configuration and dataset
Expand All @@ -36,8 +30,8 @@
import _ from 'lodash';
import { computed, watch, ref, shallowRef, onMounted, toRef } from 'vue';
import Button from 'primevue/button';

import TeraOperatorPlaceholder from '@/components/operator/tera-operator-placeholder.vue';
import TeraProgressSpinner from '@/components/widgets/tera-progress-spinner.vue';
import {
getRunResultCSV,
pollAction,
Expand Down Expand Up @@ -76,6 +70,7 @@ import { useCharts } from '@/composables/useCharts';
import { filterChartSettingsByVariables } from '@/services/chart-settings';
import { ChartSettingType } from '@/types/common';
import { parseCsvAsset } from '@/utils/csv';
import TeraNodeCharts from '../tera-node-charts.vue';
import type { CalibrationOperationStateCiemss } from './calibrate-operation';
import { CalibrationOperationCiemss } from './calibrate-operation';
import { renameFnGenerator, usePreparedChartInputs, getSelectedOutputMapping } from './calibrate-utils';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,11 @@
<template v-if="!isRunInProgress">
<AccordionTab v-if="selectedEnsembleVariableSettings.length > 0" header="Ensemble variables over time">
<div class="flex flex-row" v-for="setting of selectedEnsembleVariableSettings" :key="setting.id">
<vega-chart
v-for="(spec, index) of ensembleVariableCharts[setting.id]"
:key="index"
expandable
:are-embed-actions-visible="true"
:visualization-spec="spec"
<tera-node-charts
mloppie marked this conversation as resolved.
Show resolved Hide resolved
:node="node"
:prepared-charts="ensembleVariableCharts"
:chart-settings="selectedEnsembleVariableSettings"
:expandable="true"
mloppie marked this conversation as resolved.
Show resolved Hide resolved
/>
</div>
</AccordionTab>
Expand Down Expand Up @@ -397,6 +396,7 @@ import {
EnsembleErrorData,
fetchModelConfigurations
} from './calibrate-ensemble-util';
import TeraNodeCharts from '../tera-node-charts.vue';

const props = defineProps<{
node: WorkflowNode<CalibrateEnsembleCiemssOperationState>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@
:visualization-spec="lossChartSpec"
/>
<div v-if="outputData">
<vega-chart
v-for="(chart, index) of ensembleVariableCharts"
:key="index"
:interactive="false"
:visualization-spec="chart"
/>
<vega-chart v-for="(chart, index) of ensembleVariableCharts" :key="index" :visualization-spec="chart" />
mloppie marked this conversation as resolved.
Show resolved Hide resolved
</div>
<tera-progress-spinner
v-if="inProgressCalibrationId || inProgressForecastId"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import { isEmpty } from 'lodash';
import { Dataset } from '@/types/Types';
import { WorkflowPortStatus } from '@/types/workflow';
import { renameFnGenerator } from '@/components/workflow/ops/calibrate-ciemss/calibrate-utils';

import { createRankingInterventionsChart } from '@/services/charts';
import { DATASET_VAR_NAME_PREFIX, getDatasetResultCSV, mergeResults, getDataset } from '@/services/dataset';
import {
DataArray,
getRunResultCSV,
parsePyCiemssMap,
processAndSortSamplesByTimepoint
processAndSortSamplesByTimepoint,
getRunResultCSV
} from '@/services/models/simulation-service';
import { DATASET_VAR_NAME_PREFIX, getDatasetResultCSV, mergeResults } from '@/services/dataset';
import { getInterventionPolicyById } from '@/services/intervention-policy';
import { getModelConfigurationById } from '@/services/model-configurations';

import { ChartData } from '@/composables/useCharts';
import { PlotValue } from './compare-datasets-operation';

import { PlotValue, TimepointOption, RankOption } from './compare-datasets-operation';

interface DataResults {
results: DataArray[];
Expand Down Expand Up @@ -169,3 +177,161 @@ export function buildChartData(
numComparableDatasets: datasets.length
};
}

export function generateRankingCharts(
rankingCriteriaCharts,
rankingResultsChart,
props,
modelConfigIdToInterventionPolicyIdMap,
chartData,
interventionPolicies
) {
// Reset charts
rankingCriteriaCharts.value = [];
rankingResultsChart.value = null;

// Might be uneccessary
const commonInterventionPolicyIds = props.node.state.criteriaOfInterestCards
.map(({ selectedConfigurationId }) => {
if (!selectedConfigurationId) return [];
return modelConfigIdToInterventionPolicyIdMap.value?.[selectedConfigurationId] ?? [];
})
.flat();
const allRankedCriteriaValues: { score: number; name: string }[][] = [];

props.node.state.criteriaOfInterestCards.forEach((card) => {
if (!card.selectedConfigurationId || !chartData.value) return;

const pointOfComparison =
card.timepoint === TimepointOption.FIRST
? chartData.value.resultSummary[0]
: chartData.value.resultSummary[chartData.value.resultSummary.length - 1];

const rankingCriteriaValues: { score: number; name: string }[] = [];
interventionPolicies.value.forEach((policy, index) => {
// Skip this intervention policy if a configuration is not using it
if (!policy.id || !policy.name || !commonInterventionPolicyIds.includes(policy.id) || !card.selectedVariable) {
return;
}

rankingCriteriaValues.push({
score: pointOfComparison[`${chartData.value?.pyciemssMap[card.selectedVariable]}_mean:${index}`] ?? 0,
name: policy.name ?? ''
});
});

const sortedRankingCriteriaValues =
card.rank === RankOption.MAXIMUM
? rankingCriteriaValues.sort((a, b) => b.score - a.score)
: rankingCriteriaValues.sort((a, b) => a.score - b.score);

sortedRankingCriteriaValues.forEach((value, index) => {
value.score = index + 1;
});

rankingCriteriaCharts.value.push(createRankingInterventionsChart(sortedRankingCriteriaValues, card.name));
allRankedCriteriaValues.push(sortedRankingCriteriaValues);
});

// Sum up the scores of the same intervention policy
const scoreMap: Record<string, number> = {};
allRankedCriteriaValues.flat().forEach(({ score, name }) => {
if (scoreMap[name]) {
scoreMap[name] += score;
} else {
scoreMap[name] = score;
}
});

const rankingResultsValues = Object.keys(scoreMap)
.map((name) => ({
name,
score: scoreMap[name]
}))
.sort((a, b) => a.score - b.score);

rankingResultsChart.value = createRankingInterventionsChart(rankingResultsValues, '');
}

export async function generateImpactCharts(
chartData,
datasets,
datasetResults,
baselineDatasetIndex,
selectedPlotType
) {
chartData.value = buildChartData(
datasets.value,
datasetResults.value,
baselineDatasetIndex.value,
selectedPlotType.value
);
}
mloppie marked this conversation as resolved.
Show resolved Hide resolved

// TODO: this should probably be split up into smaller functions but for now it's at least not duplicated in the node and drilldown
export async function initialize(
props,
isFetchingDatasets,
datasets,
datasetResults,
modelConfigIdToInterventionPolicyIdMap,
chartData,
baselineDatasetIndex,
selectedPlotType,
modelConfigurations,
interventionPolicies,
rankingCriteriaCharts,
rankingResultsChart
) {
const { inputs } = props.node;
const datasetInputs = inputs.filter(
(input) => input.type === 'datasetId' && input.status === WorkflowPortStatus.CONNECTED
);
const datasetPromises = datasetInputs.map((input) => getDataset(input.value![0]));
isFetchingDatasets.value = true;
await Promise.all(datasetPromises).then((ds) => {
ds.forEach((dataset) => {
// Add dataset
if (!dataset) return;
datasets.value.push(dataset);

// Collect model configuration id and intervention policy id
const modelConfigurationId: string | undefined = dataset.metadata?.simulationAttributes?.modelConfigurationId;
const interventionPolicyId: string | undefined = dataset.metadata?.simulationAttributes?.interventionPolicyId;

if (!modelConfigurationId) return;
if (!modelConfigIdToInterventionPolicyIdMap.value[modelConfigurationId]) {
modelConfigIdToInterventionPolicyIdMap.value[modelConfigurationId] = [];
}
if (!interventionPolicyId) return;
modelConfigIdToInterventionPolicyIdMap.value[modelConfigurationId].push(interventionPolicyId);
});
});
// Fetch the results
datasetResults.value = await fetchDatasetResults(datasets.value);
isFetchingDatasets.value = false;

await generateImpactCharts(chartData, datasets, datasetResults, baselineDatasetIndex, selectedPlotType);
const modelConfigurationIds = Object.keys(modelConfigIdToInterventionPolicyIdMap.value);
if (isEmpty(modelConfigurationIds)) return;
const modelConfigurationPromises = modelConfigurationIds.map((id) => getModelConfigurationById(id));
await Promise.all(modelConfigurationPromises).then((configs) => {
modelConfigurations.value = configs.filter((config) => config !== null);
});

const interventionPolicyIds = Object.values(modelConfigIdToInterventionPolicyIdMap.value).flat();
if (isEmpty(interventionPolicyIds)) return;
const interventionPolicyPromises = interventionPolicyIds.map((id) => getInterventionPolicyById(`${id}`));
await Promise.all(interventionPolicyPromises).then((policies) => {
interventionPolicies.value = policies.filter((policy) => policy !== null);
});

generateRankingCharts(
rankingCriteriaCharts,
rankingResultsChart,
props,
modelConfigIdToInterventionPolicyIdMap,
chartData,
interventionPolicies
);
}
Loading