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

Improve usage of status writer for settings and view in SimulationTimeSeriesMatrix #424

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 21 additions & 4 deletions frontend/src/modules/SimulationTimeSeriesMatrix/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { EnsembleIdent } from "@framework/EnsembleIdent";
import { Parameter, ParameterIdent, ParameterType } from "@framework/EnsembleParameters";
import { EnsembleSet } from "@framework/EnsembleSet";
import { ModuleFCProps } from "@framework/Module";
import { useSettingsStatusWriter } from "@framework/StatusWriter";
import { useEnsembleSet } from "@framework/WorkbenchSession";
import { MultiEnsembleSelect } from "@framework/components/MultiEnsembleSelect";
import { ParameterListFilter } from "@framework/components/ParameterListFilter";
Expand Down Expand Up @@ -39,6 +40,7 @@ import {
VisualizationModeEnumToStringMapping,
} from "./state";
import { EnsembleVectorListsHelper } from "./utils/ensemblesVectorListHelper";
import { joinStringArrayToHumanReadableString } from "./utils/stringUtils";

enum StatisticsType {
INDIVIDUAL = "Individual",
Expand All @@ -47,6 +49,7 @@ enum StatisticsType {

export function settings({ moduleContext, workbenchSession }: ModuleFCProps<State>) {
const ensembleSet = useEnsembleSet(workbenchSession);
const statusWriter = useSettingsStatusWriter(moduleContext);

// Store state/values
const [resampleFrequency, setResamplingFrequency] = moduleContext.useStoreState("resamplingFrequency");
Expand Down Expand Up @@ -125,19 +128,33 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps<Stat
setStatisticsType(computedStatisticsType);
}

// Set warning for selected vectors not existing in a selected ensemble
for (const ensembleIdent of selectedEnsembleIdents) {
const nonExistingVectors = selectedVectorNames.filter(
(vector) => !ensembleVectorListsHelper.isVectorInEnsemble(ensembleIdent, vector)
);
if (nonExistingVectors.length === 0) {
continue;
}

const ensembleStr = ensembleSet.findEnsemble(ensembleIdent)?.getDisplayName() ?? ensembleIdent.toString();
const vectorArrayStr = joinStringArrayToHumanReadableString(nonExistingVectors);
statusWriter.addWarning(`Vector ${vectorArrayStr} does not exist in ensemble ${ensembleStr}`);
}

React.useEffect(
function propagateVectorSpecsToView() {
const newVectorSpecifications: VectorSpec[] = [];
for (const ensemble of selectedEnsembleIdents) {
for (const ensembleIdent of selectedEnsembleIdents) {
for (const vector of selectedVectorNames) {
if (!ensembleVectorListsHelper.isVectorInEnsemble(ensemble, vector)) {
if (!ensembleVectorListsHelper.isVectorInEnsemble(ensembleIdent, vector)) {
continue;
}

newVectorSpecifications.push({
ensembleIdent: ensemble,
ensembleIdent: ensembleIdent,
vectorName: vector,
hasHistoricalVector: ensembleVectorListsHelper.hasHistoricalVector(ensemble, vector),
hasHistoricalVector: ensembleVectorListsHelper.hasHistoricalVector(ensembleIdent, vector),
});
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Utility function to make a display friendly string from an array of strings.
*
* Example: ["a", "b", "c"] -> "a, b and c"
*/
export function joinStringArrayToHumanReadableString(stringArray: string[]): string {
if (stringArray.length === 0) return "";
if (stringArray.length === 1) return stringArray[0];

return stringArray.slice(0, -1).join(", ") + " and " + stringArray[stringArray.length - 1];
}
52 changes: 37 additions & 15 deletions frontend/src/modules/SimulationTimeSeriesMatrix/view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export const view = ({ moduleContext, workbenchSession, workbenchSettings }: Mod
const wrapperDivSize = useElementSize(wrapperDivRef);

const ensembleSet = useEnsembleSet(workbenchSession);

const statusWriter = useViewStatusWriter(moduleContext);

// Store values
Expand Down Expand Up @@ -68,20 +67,26 @@ export const view = ({ moduleContext, workbenchSession, workbenchSettings }: Mod
vectorSpecificationsWithHistoricalData?.some((vec) => vec.hasHistoricalVector) ?? false
);

// Get fetching status from queries
const isQueryFetching =
vectorDataQueries.some((query) => query.isFetching) ||
vectorStatisticsQueries.some((query) => query.isFetching) ||
historicalVectorDataQueries.some((query) => query.isFetching);

statusWriter.setLoading(isQueryFetching);

const hasQueryError =
vectorDataQueries.some((query) => query.isError) ||
vectorStatisticsQueries.some((query) => query.isError) ||
historicalVectorDataQueries.some((query) => query.isError);
if (hasQueryError) {
statusWriter.addError("One or more queries have an error state.");
return <ContentError>One or more queries have an error state.</ContentError>;
// Get error/warning status from queries
const hasRealizationsQueryError = vectorDataQueries.some((query) => query.isError);
const hasStatisticsQueryError = vectorStatisticsQueries.some((query) => query.isError);
const hasHistoricalVectorQueryError = historicalVectorDataQueries.some((query) => query.isError);
if (hasRealizationsQueryError) {
statusWriter.addError("One or more realization data queries have an error state.");
}
if (hasStatisticsQueryError) {
statusWriter.addError("One or more statistics data queries have an error state.");
}
if (hasHistoricalVectorQueryError) {
statusWriter.addWarning("One or more historical data queries have an error state.");
}

// Map vector specifications and queries with data
Expand Down Expand Up @@ -114,12 +119,25 @@ export const view = ({ moduleContext, workbenchSession, workbenchSettings }: Mod
// Create parameter color scale helper
const doColorByParameter =
colorRealizationsByParameter &&
visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS &&
parameterIdent !== null &&
selectedEnsembles.some((ensemble) => ensemble.getParameters().hasParameter(parameterIdent));
const ensemblesParameterColoring = doColorByParameter
? new EnsemblesContinuousParameterColoring(selectedEnsembles, parameterIdent, parameterColorScale)
: null;

// Set warning for ensembles without selected parameter when coloring is enabled
if (doColorByParameter && ensemblesParameterColoring) {
const ensemblesWithoutParameter = selectedEnsembles.filter(
(ensemble) => !ensemblesParameterColoring.hasParameterForEnsemble(ensemble.getIdent())
);
for (const ensemble of ensemblesWithoutParameter) {
statusWriter.addWarning(
`Ensemble ${ensemble.getDisplayName()} does not have parameter ${ensemblesParameterColoring.getParameterDisplayName()}`
);
}
}

// Callback function for ensemble display name
function makeEnsembleDisplayName(ensembleIdent: EnsembleIdent): string {
const ensembleNameCount = selectedEnsembles.filter(
Expand Down Expand Up @@ -193,16 +211,20 @@ export const view = ({ moduleContext, workbenchSession, workbenchSettings }: Mod
subplotBuilder.addHistoryTraces(loadedVectorSpecificationsAndHistoricalData);
}

// TODO: Keep uirevision?
const doRenderContentError = hasRealizationsQueryError || hasStatisticsQueryError;
const plotData = subplotBuilder.createPlotData();
return (
<div className="w-full h-full" ref={wrapperDivRef}>
<Plot
key={plotData.length} // Note: To trigger re-render and remove legends when plotData is empty
data={plotData}
layout={subplotBuilder.createPlotLayout()}
config={{ scrollZoom: true }}
/>
{doRenderContentError ? (
<ContentError>One or more queries have an error state.</ContentError>
) : (
<Plot
key={plotData.length} // Note: Temporary to trigger re-render and remove legends when plotData is empty
data={plotData}
layout={subplotBuilder.createPlotLayout()}
config={{ scrollZoom: true }}
/>
)}
</div>
);
};
10 changes: 10 additions & 0 deletions frontend/tests/unit-tests/SimulationTimeSeriesMatrixUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { joinStringArrayToHumanReadableString } from "@modules/SimulationTimeSeriesMatrix/utils/stringUtils";

describe("Test of utility functions for SimulationTimeSeriesMatrix module", () => {
test("Test join string array to human readable string", () => {
expect(joinStringArrayToHumanReadableString(["a", "b", "c"])).toBe("a, b and c");
expect(joinStringArrayToHumanReadableString(["a"])).toBe("a");
expect(joinStringArrayToHumanReadableString([])).toBe("");
expect(joinStringArrayToHumanReadableString(["a", "b"])).toBe("a and b");
});
});