diff --git a/packages/client/hmi-client/src/components/workflow/ops/compare-datasets/tera-compare-datasets-drilldown.vue b/packages/client/hmi-client/src/components/workflow/ops/compare-datasets/tera-compare-datasets-drilldown.vue index a2fed6ae5d..be34e3c970 100644 --- a/packages/client/hmi-client/src/components/workflow/ops/compare-datasets/tera-compare-datasets-drilldown.vue +++ b/packages/client/hmi-client/src/components/workflow/ops/compare-datasets/tera-compare-datasets-drilldown.vue @@ -83,26 +83,14 @@ placeholder="Dataset" /> - - - - - + + + @@ -113,18 +101,7 @@ placeholder="Variable" filter v-model="data[dataset.id]" - :options=" - dataset.columns - ?.filter((ele) => ele.fileName === getFileName(dataset)) - ?.map((ele) => ele.name) - .filter( - (ele) => - !ele?.includes('median') && - !ele?.includes('std') && - !ele?.includes('min') && - !ele?.includes('max') - ) - " + :options="mappingOptions[dataset.id]" @change="constructWisTable" /> @@ -558,11 +535,8 @@ const rankingChartData = ref(null); const rankingResultsChart = ref(null); const rankingCriteriaCharts = ref([]); -const variableNames = computed(() => { - if (impactChartData.value === null) return []; - const excludes = ['timepoint_id', 'sample_id', 'timepoint_unknown']; - return Object.keys(impactChartData.value.pyciemssMap).filter((key) => !excludes.includes(key)); -}); +const variableNames = ref([]); +const mappingOptions = ref>({}); const { generateAnnotation, getChartAnnotationsByChartId, useCompareDatasetCharts } = useCharts( props.node.id, @@ -675,6 +649,9 @@ function deleteMapRow(index: number) { // TODO: Investigate sharing similar logic between constructing ate and wis tables since they are very similar // It may or may not be a good idea function constructWisTable() { + if (knobs.value.selectedGroundTruthDatasetId === null) return; + const selectedGroundTruthDatasetId = knobs.value.selectedGroundTruthDatasetId; + wisTable.value = []; wisVariableHeaders.value = []; @@ -682,13 +659,17 @@ function constructWisTable() { const observationsMap: Record = {}; const variableToTypeMap: Record = {}; + const variablesOfInterest = [ + ...new Set([...selectedVariableNames.value, ...knobs.value.mapping.map((m) => Object.values(m)).flat()]) + ]; + datasetResults.value?.summaryResults.forEach((summaryResult) => { Object.keys(summaryResult[0]).forEach((key) => { if ( key.includes('_param_') || !key.includes('_mean:') || - // Skip if the variable is not selected in output settings - !selectedVariableNames.value.some((variableName) => { + // Skip if the variable is not selected in output settings or attached to the ground truth dataset in your mapping + !variablesOfInterest.some((variableName) => { if (key.includes(variableName)) { if (!variableToTypeMap[variableName]) { variableToTypeMap[variableName] = key.includes('_observable_state_') ? '_observable_state_' : '_state_'; @@ -716,22 +697,18 @@ function constructWisTable() { Object.entries(variableToTypeMap).forEach(([variableName, type]) => { const key = `${variableName}${type}mean:${index}`; - if (!observationsKeyNames.includes(key)) return; - - let groundTruthKey = `${key.slice(0, -1)}${groundTruthDatasetIndex.value}`; - // Use mapping to get ground truth key - if (!observationsKeyNames.includes(groundTruthKey)) { - let isFound = false; - knobs.value.mapping.forEach((mapping) => { - if ( - !isFound && - Object.values(mapping).includes(key.slice(0, -2)) && - knobs.value.selectedGroundTruthDatasetId - ) { - groundTruthKey = `${mapping[knobs.value.selectedGroundTruthDatasetId]}:${groundTruthDatasetIndex.value}`; - isFound = true; - } - }); + if (!observationsKeyNames.includes(key)) { + return; + } + + const datasetMapping = knobs.value.mapping.find((m) => Object.values(m).includes(variableName)); + if (!datasetMapping) return; + + const groundTruthVariableName = datasetMapping[selectedGroundTruthDatasetId]; + const groundTruthKey = `${groundTruthVariableName}${type}mean:${groundTruthDatasetIndex.value}`; + + if (!observationsMap[groundTruthKey]) { + return; } const wis = getWeightedIntervalScore( @@ -746,7 +723,14 @@ function constructWisTable() { } const totalMean = mean(wis.total); + + // FIXME: For now I am assigning to the value to the ground truth column and the variable column + // The table columns that end up actually appearing are the variables chosen in the output settings + // But the ground truth may not necessarily match up with what's chosen in the output settings + // So this is kind of a lazy solution that'll always work, (later we'll see how we exactly want to sync the mapping and the output settings selector) + wisRow[groundTruthVariableName] = totalMean; wisRow[variableName] = totalMean; + wisValues.push(totalMean); }); wisRow.overall = mean(wisValues); @@ -781,6 +765,39 @@ onMounted(async () => { rankingResultsChart ); + // Prepare variable dropdowns + let allVariableNames: string[] = []; + if (impactChartData.value) { + allVariableNames = Object.keys(impactChartData.value.pyciemssMap); + variableNames.value = allVariableNames.filter( + (key) => !['timepoint_id', 'sample_id', 'timepoint_unknown'].includes(key) + ); + } + + const swappedPyCiemssMap: Record = {}; + Object.entries(impactChartData.value?.pyciemssMap ?? {}).forEach(([key, value]) => { + swappedPyCiemssMap[value] = key; + }); + const pyciemssNames = Object.keys(swappedPyCiemssMap); + + datasets.value.forEach((dataset) => { + const datasetId = dataset.id as string; + mappingOptions.value[datasetId] = []; + + if (!dataset.columns) return; + dataset.columns.forEach((column) => { + if (!column.name || column.fileName !== getFileName(dataset)) return; + + let option = ''; + if (pyciemssNames.includes(column.name)) option = swappedPyCiemssMap[column.name]; + else if (pyciemssNames.includes(`data/${column.name}`)) option = swappedPyCiemssMap[`data/${column.name}`]; + if (!option) return; + + mappingOptions.value[datasetId].push(option); + }); + }); + + // Construct tables constructATETable(); if (isEmpty(knobs.value.mapping)) addMapping();