From 5ed7e63793237be71469f27c4a4375ed62a0290f Mon Sep 17 00:00:00 2001 From: Ruben Thoms <69145689+rubenthoms@users.noreply.github.com> Date: Mon, 19 Aug 2024 13:38:52 +0200 Subject: [PATCH 1/7] ParameterDistributionPlot now using ensemble colors (#672) --- .../ParameterDistributionMatrix/typesAndEnums.ts | 1 + .../view/components/ParameterDistributionPlot.tsx | 14 +++++--------- .../ParameterDistributionMatrix/view/view.tsx | 9 +-------- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/frontend/src/modules/ParameterDistributionMatrix/typesAndEnums.ts b/frontend/src/modules/ParameterDistributionMatrix/typesAndEnums.ts index 2da7d740a..a5d9cb54c 100644 --- a/frontend/src/modules/ParameterDistributionMatrix/typesAndEnums.ts +++ b/frontend/src/modules/ParameterDistributionMatrix/typesAndEnums.ts @@ -2,6 +2,7 @@ import { ParameterIdent } from "@framework/EnsembleParameters"; export type EnsembleParameterRealizationsAndValues = { ensembleDisplayName: string; + ensembleColor: string; realizations: number[]; values: number[]; }; diff --git a/frontend/src/modules/ParameterDistributionMatrix/view/components/ParameterDistributionPlot.tsx b/frontend/src/modules/ParameterDistributionMatrix/view/components/ParameterDistributionPlot.tsx index 6448f8f8b..cb7e00bd2 100644 --- a/frontend/src/modules/ParameterDistributionMatrix/view/components/ParameterDistributionPlot.tsx +++ b/frontend/src/modules/ParameterDistributionMatrix/view/components/ParameterDistributionPlot.tsx @@ -9,7 +9,6 @@ import { ParameterDataArr, ParameterDistributionPlotType } from "../../typesAndE type ParameterDistributionPlotProps = { dataArr: ParameterDataArr[]; - ensembleColors: Map; plotType: ParameterDistributionPlotType; showIndividualRealizationValues: boolean; showPercentilesAndMeanLines: boolean; @@ -36,7 +35,6 @@ export const ParameterDistributionPlot: React.FC if (shouldShowLegend) { addedLegendNames.add(ensembleData.ensembleDisplayName); } - const ensembleColor = props.ensembleColors.get(ensembleData.ensembleDisplayName); const distributionTrace = { x: ensembleData.values, @@ -44,7 +42,7 @@ export const ParameterDistributionPlot: React.FC spanmode: "hard", name: ensembleData.ensembleDisplayName, legendgroup: ensembleData.ensembleDisplayName, - marker: { color: ensembleColor }, + marker: { color: ensembleData.ensembleColor }, xaxis: `x${subplotIndex}`, yaxis: `y${subplotIndex}`, showlegend: shouldShowLegend, @@ -65,7 +63,7 @@ export const ParameterDistributionPlot: React.FC ensembleData.values, yPosition, ensembleData.ensembleDisplayName, - ensembleColor, + ensembleData.ensembleColor, subplotIndex ) ); @@ -93,7 +91,7 @@ export const ParameterDistributionPlot: React.FC hoverinfo: "x+text+name", mode: "markers", marker: { - color: props.ensembleColors.get(ensembleData.ensembleDisplayName), + color: ensembleData.ensembleColor, symbol: "line-ns-open", }, showlegend: false, @@ -123,8 +121,6 @@ export const ParameterDistributionPlot: React.FC throw new Error("Realizations and values must have the same length"); } - const ensembleColor = props.ensembleColors.get(ensembleData.ensembleDisplayName); - const verticalPosition = index * (2 + 1); // 2 is the height of each box + 1 space const hoverText = ensembleData.values.map( (_, index) => `Realization: ${ensembleData.realizations[index]}` @@ -135,7 +131,7 @@ export const ParameterDistributionPlot: React.FC type: "box", name: ensembleData.ensembleDisplayName, legendgroup: ensembleData.ensembleDisplayName, - marker: { color: ensembleColor }, + marker: { color: ensembleData.ensembleColor }, xaxis: `x${subplotIndex}`, yaxis: `y${subplotIndex}`, showlegend: shouldShowLegend, @@ -157,7 +153,7 @@ export const ParameterDistributionPlot: React.FC ensembleData.values, verticalPosition, ensembleData.ensembleDisplayName, - ensembleColor, + ensembleData.ensembleColor, subplotIndex ) ); diff --git a/frontend/src/modules/ParameterDistributionMatrix/view/view.tsx b/frontend/src/modules/ParameterDistributionMatrix/view/view.tsx index 41e0f1c14..3a69a9a6b 100644 --- a/frontend/src/modules/ParameterDistributionMatrix/view/view.tsx +++ b/frontend/src/modules/ParameterDistributionMatrix/view/view.tsx @@ -36,18 +36,10 @@ export function View(props: ModuleViewProps) { filterEnsembleRealizationsFunc ); - const colorSet = props.workbenchSettings.useColorSet(); - const ensembleColors = new Map(); - ensembleSet.getEnsembleArr().forEach((ensemble, index) => { - const color = index === 0 ? colorSet.getFirstColor() : colorSet.getNextColor(); - ensembleColors.set(ensemble.getDisplayName(), color); - }); - return (
Date: Tue, 20 Aug 2024 09:22:11 +0200 Subject: [PATCH 2/7] Remove `StateStore` and `ModuleAtoms`, add `InterfaceEffects` and refactor modules accordingly (#679) --- frontend/package-lock.json | 9 + frontend/package.json | 1 + frontend/src/App.tsx | 55 +- frontend/src/framework/Module.tsx | 199 +++--- frontend/src/framework/ModuleContext.ts | 129 +--- frontend/src/framework/ModuleInstance.ts | 212 +++--- frontend/src/framework/ModuleRegistry.ts | 72 +- frontend/src/framework/StateStore.ts | 104 --- frontend/src/framework/StatusWriter.ts | 4 +- frontend/src/framework/SyncSettings.ts | 4 +- ...UniDirectionalModuleComponentsInterface.ts | 8 +- frontend/src/framework/Workbench.ts | 8 +- .../internal/ModuleNotFoundPlaceholder.tsx | 12 +- .../applyInterfaceEffects.tsx | 25 + .../components/ApplyInterfaceEffects/index.ts | 0 .../channelReceiverNodesWrapper.tsx | 2 +- .../ViewWrapper/private-components/header.tsx | 2 +- .../private-components/viewContent.tsx | 21 +- .../ViewWrapper/viewWrapper.tsx | 2 +- .../ErrorBoundary/errorBoundary.tsx | 2 +- .../private-components/moduleSettings.tsx | 21 +- .../private-components/modulesList.tsx | 2 +- .../ModuleInstanceLog/moduleInstanceLog.tsx | 2 +- .../internal/hooks/workbenchHooks.ts | 4 +- ...ttingsToViewInterface.ts => interfaces.ts} | 57 +- frontend/src/modules/3DViewer/loadModule.tsx | 12 +- .../src/modules/3DViewer/registerModule.ts | 5 +- .../3DViewer/settings/atoms/baseAtoms.ts | 5 + .../3DViewer/settings/atoms/derivedAtoms.ts | 51 +- .../settings/atoms/interfaceEffects.ts | 17 + .../3DViewer/settings/atoms/queryAtoms.ts | 3 +- .../modules/3DViewer/settings/settings.tsx | 17 +- .../3DViewer/sharedAtoms/sharedAtoms.ts | 71 -- frontend/src/modules/3DViewer/state.ts | 1 - .../src/modules/3DViewer/typesAndEnums.ts | 11 - .../modules/3DViewer/view/atoms/baseAtoms.ts | 11 + .../3DViewer/view/atoms/derivedAtoms.ts | 24 +- .../3DViewer/view/atoms/interfaceEffects.ts | 28 + .../modules/3DViewer/view/atoms/queryAtoms.ts | 5 +- .../view/components/HoverUpdateWrapper.tsx | 2 +- .../SyncedSettingsUpdateWrapper.tsx | 2 +- frontend/src/modules/3DViewer/view/view.tsx | 34 +- .../DbgWorkbenchSpy/implementation.tsx | 18 +- .../src/modules/DbgWorkbenchSpy/interfaces.ts | 15 + .../modules/DbgWorkbenchSpy/loadModule.tsx | 11 +- .../modules/DbgWorkbenchSpy/registerModule.ts | 4 +- .../modules/DistributionPlot/interfaces.ts | 20 + .../modules/DistributionPlot/loadModule.tsx | 14 +- .../DistributionPlot/registerModule.ts | 4 +- .../settings/atoms/baseAtoms.ts | 7 + .../{ => settings}/settings.tsx | 15 +- .../{state.ts => typesAndEnums.ts} | 6 - .../src/modules/DistributionPlot/view.tsx | 11 +- ...ttingsToViewInterface.ts => interfaces.ts} | 10 +- .../src/modules/FlowNetwork/loadModule.tsx | 6 +- .../src/modules/FlowNetwork/registerModule.ts | 4 +- .../modules/FlowNetwork/settings/settings.tsx | 4 +- frontend/src/modules/FlowNetwork/view.tsx | 4 +- .../modules/InplaceVolumetrics/interfaces.ts | 34 + .../modules/InplaceVolumetrics/loadModule.tsx | 15 +- .../InplaceVolumetrics/registerModule.ts | 4 +- .../settings/atoms/baseAtoms.ts | 11 + .../{ => settings}/settings.tsx | 18 +- .../src/modules/InplaceVolumetrics/state.ts | 11 - .../src/modules/InplaceVolumetrics/view.tsx | 16 +- ...ttingsToViewInterface.ts => interfaces.ts} | 14 +- .../src/modules/Intersection/loadModule.tsx | 17 +- .../modules/Intersection/registerModule.ts | 6 +- .../Intersection/settings/settings.tsx | 8 +- frontend/src/modules/Intersection/state.ts | 1 - .../src/modules/Intersection/typesAndEnums.ts | 7 + .../view/atoms/atomDefinitions.ts | 137 ---- .../Intersection/view/atoms/baseAtoms.ts | 9 + .../Intersection/view/atoms/derivedAtoms.ts | 100 +++ .../view/atoms/interfaceEffects.ts | 28 + .../Intersection/view/atoms/queryAtoms.ts | 22 + .../view/components/layersWrapper.tsx | 6 +- .../view/components/readoutWrapper.tsx | 7 +- .../view/components/viewportWrapper.tsx | 7 +- .../src/modules/Intersection/view/view.tsx | 18 +- frontend/src/modules/Map/MapState.ts | 5 - frontend/src/modules/Map/interfaces.ts | 16 + frontend/src/modules/Map/loadModule.tsx | 14 +- frontend/src/modules/Map/registerModule.ts | 4 +- .../modules/Map/settings/atoms/baseAtoms.ts | 5 + .../settings.tsx} | 13 +- .../src/modules/Map/{MapView.tsx => view.tsx} | 7 +- frontend/src/modules/MyModule/interfaces.ts | 34 + frontend/src/modules/MyModule/loadModule.tsx | 17 +- .../src/modules/MyModule/registerModule.ts | 4 +- .../MyModule/settings/atoms/baseAtoms.ts | 9 + .../MyModule/{ => settings}/settings.tsx | 20 +- frontend/src/modules/MyModule/state.ts | 9 - frontend/src/modules/MyModule/view.tsx | 16 +- frontend/src/modules/MyModule2/interfaces.ts | 17 + frontend/src/modules/MyModule2/loadModule.tsx | 9 +- .../src/modules/MyModule2/registerModule.ts | 4 +- frontend/src/modules/MyModule2/settings.tsx | 4 +- frontend/src/modules/MyModule2/state.ts | 23 - frontend/src/modules/MyModule2/view.tsx | 6 +- ...ttingsToViewInterface.ts => interfaces.ts} | 8 +- .../loadModule.tsx | 7 +- .../registerModule.tsx | 5 +- .../settings/settings.tsx | 12 +- .../ParameterDistributionMatrix/state.ts | 1 - .../ParameterDistributionMatrix/view/view.tsx | 5 +- frontend/src/modules/Pvt/interfaces.ts | 41 ++ frontend/src/modules/Pvt/loadModule.tsx | 12 +- frontend/src/modules/Pvt/registerModule.tsx | 5 +- .../src/modules/Pvt/settings/settings.tsx | 5 +- frontend/src/modules/Pvt/state.ts | 1 - frontend/src/modules/Pvt/view.tsx | 9 +- frontend/src/modules/Rft/interfaces.ts | 16 + frontend/src/modules/Rft/loadModule.tsx | 8 +- frontend/src/modules/Rft/registerModule.tsx | 4 +- frontend/src/modules/Rft/settings.tsx | 9 +- .../modules/Rft/settings/atoms/baseAtoms.ts | 5 + .../Rft/{state.ts => typesAndEnums.ts} | 8 +- frontend/src/modules/Rft/view.tsx | 6 +- .../modules/SeismicIntersection/interfaces.ts | 51 ++ .../SeismicIntersection/loadModule.tsx | 16 +- .../SeismicIntersection/queryHooks.tsx | 14 - .../SeismicIntersection/registerModule.ts | 4 +- .../settings/atoms/baseAtoms.ts | 12 + .../settings/hooks/queryHooks.tsx | 19 + .../{ => settings}/settings.tsx | 53 +- .../src/modules/SeismicIntersection/state.ts | 25 - .../src/modules/SeismicIntersection/types.ts | 16 - .../SeismicIntersection/typesAndEnums.ts | 28 + .../src/modules/SeismicIntersection/view.tsx | 21 +- ...ttingsToViewInterface.ts => interfaces.ts} | 6 +- .../SimulationTimeSeries/loadModule.tsx | 18 +- .../SimulationTimeSeries/registerModule.ts | 7 +- .../settings/atoms/atomDefinitions.ts | 7 - .../settings/settings.tsx | 7 +- .../src/modules/SimulationTimeSeries/state.ts | 1 - .../view/atoms/atomDefinitions.ts | 372 ----------- .../view/atoms/baseAtoms.ts | 15 + .../view/atoms/derivedAtoms.ts | 126 ++++ .../view/atoms/interfaceEffects.ts | 43 ++ .../view/atoms/queryAtoms.ts | 204 ++++++ .../hooks/useMakeEnsembleDisplayNameFunc.ts | 8 +- .../hooks/useMakeViewStatusWriterMessages.ts | 24 +- .../view/hooks/usePublishToDataChannels.ts | 24 +- .../view/hooks/useSubplotBuilder.ts | 31 +- .../SimulationTimeSeries/view/view.tsx | 18 +- .../SimulationTimeSeriesMatrix/channelDefs.ts | 13 - .../dataGenerators.ts | 53 -- .../SimulationTimeSeriesMatrix/loadModule.tsx | 26 - .../SimulationTimeSeriesMatrix/preview.svg | 9 - .../SimulationTimeSeriesMatrix/preview.tsx | 6 - .../SimulationTimeSeriesMatrix/queryHooks.tsx | 225 ------- .../registerModule.ts | 23 - .../SimulationTimeSeriesMatrix/settings.tsx | 520 --------------- .../SimulationTimeSeriesMatrix/state.ts | 79 --- .../createVectorTracesUtils.ts | 381 ----------- .../PlotlyTraceUtils/fanchartPlotting.ts | 275 -------- .../PlotlyTraceUtils/statisticsPlotting.ts | 240 ------- .../utils/colorUtils.ts | 28 - .../ensemblesContinuousParameterColoring.ts | 74 --- .../utils/ensemblesVectorListHelper.ts | 85 --- .../getContinuousAndNonConstantParameters.ts | 34 - .../utils/stringUtils.ts | 11 - .../utils/subplotBuilder.ts | 614 ------------------ .../utils/timeSeriesPlotData.ts | 9 - .../vectorSpecificationsAndQueriesUtils.ts | 83 --- .../SimulationTimeSeriesMatrix/view.tsx | 364 ----------- .../interfaces.ts | 51 ++ .../loadModule.tsx | 19 +- .../queryHooks.tsx | 20 +- .../registerModule.ts | 4 +- .../settings/atoms/baseAtoms.ts | 12 + .../settings/hooks/queryHooks.tsx | 19 + .../{ => settings}/settings.tsx | 33 +- .../SimulationTimeSeriesSensitivity/state.ts | 18 - .../typesAndEnums.ts | 7 + .../SimulationTimeSeriesSensitivity/view.tsx | 23 +- .../components/esvIntersection.tsx | 2 +- .../interfaces.ts | 51 ++ .../loadModule.tsx | 22 +- .../registerModule.ts | 4 +- .../settings/atoms/baseAtoms.ts | 16 + .../components/intersectionSettings.tsx | 2 +- .../components/realizationsSelect.tsx | 0 .../{ => settings}/settings.tsx | 33 +- .../state.ts | 2 +- .../{types.ts => typesAndEnums.ts} | 0 .../view.tsx | 16 +- .../components/AggregationSelector/index.ts | 2 - .../syncedSubsurfaceViewer.tsx | 2 +- .../src/modules/SubsurfaceMap/interfaces.ts | 47 ++ .../src/modules/SubsurfaceMap/loadModule.tsx | 20 +- .../modules/SubsurfaceMap/registerModule.ts | 4 +- .../SubsurfaceMap/settings/atoms/baseAtoms.ts | 12 + .../components}/aggregationSelector.tsx | 0 .../SubsurfaceMap/{ => settings}/settings.tsx | 53 +- frontend/src/modules/SubsurfaceMap/state.ts | 13 - frontend/src/modules/SubsurfaceMap/view.tsx | 21 +- .../src/modules/TornadoChart/interfaces.ts | 62 ++ .../src/modules/TornadoChart/loadModule.tsx | 25 +- .../modules/TornadoChart/registerModule.ts | 8 +- .../TornadoChart/settings/atoms/baseAtoms.ts | 12 + .../settings/atoms/interfaceEffects.ts | 11 + .../TornadoChart/{ => settings}/settings.tsx | 26 +- frontend/src/modules/TornadoChart/state.ts | 20 - .../src/modules/TornadoChart/typesAndEnums.ts | 9 + .../TornadoChart/view/atoms/baseAtoms.ts | 5 + .../components}/sensitivityChart.tsx | 4 +- .../components}/sensitivityTable.tsx | 4 +- .../utils}/sensitivityResponseCalculator.ts | 0 .../modules/TornadoChart/{ => view}/view.tsx | 32 +- .../src/modules/WellCompletions/interfaces.ts | 27 + .../modules/WellCompletions/loadModule.tsx | 12 +- .../modules/WellCompletions/registerModule.ts | 4 +- .../settings/atoms/baseAtoms.ts | 8 + .../{ => settings/hooks}/queryHooks.tsx | 0 .../{ => settings}/settings.tsx | 18 +- .../modules/WellCompletions/typesAndEnums.ts | 5 + frontend/src/modules/WellCompletions/view.tsx | 11 +- frontend/src/modules/registerAllModules.ts | 1 - frontend/src/templates/oatTimeSeries.ts | 2 +- ...t.ts => SimulationTimeSeriesUtils.test.ts} | 4 +- frontend/tests/unit/stateStore.test.ts | 48 -- 223 files changed, 2385 insertions(+), 5131 deletions(-) delete mode 100644 frontend/src/framework/StateStore.ts create mode 100644 frontend/src/framework/internal/components/ApplyInterfaceEffects/applyInterfaceEffects.tsx create mode 100644 frontend/src/framework/internal/components/ApplyInterfaceEffects/index.ts rename frontend/src/modules/3DViewer/{settingsToViewInterface.ts => interfaces.ts} (52%) create mode 100644 frontend/src/modules/3DViewer/settings/atoms/interfaceEffects.ts delete mode 100644 frontend/src/modules/3DViewer/sharedAtoms/sharedAtoms.ts delete mode 100644 frontend/src/modules/3DViewer/state.ts create mode 100644 frontend/src/modules/3DViewer/view/atoms/baseAtoms.ts create mode 100644 frontend/src/modules/3DViewer/view/atoms/interfaceEffects.ts create mode 100644 frontend/src/modules/DbgWorkbenchSpy/interfaces.ts create mode 100644 frontend/src/modules/DistributionPlot/interfaces.ts create mode 100644 frontend/src/modules/DistributionPlot/settings/atoms/baseAtoms.ts rename frontend/src/modules/DistributionPlot/{ => settings}/settings.tsx (88%) rename frontend/src/modules/DistributionPlot/{state.ts => typesAndEnums.ts} (59%) rename frontend/src/modules/FlowNetwork/{settingsToViewInterface.ts => interfaces.ts} (85%) create mode 100644 frontend/src/modules/InplaceVolumetrics/interfaces.ts create mode 100644 frontend/src/modules/InplaceVolumetrics/settings/atoms/baseAtoms.ts rename frontend/src/modules/InplaceVolumetrics/{ => settings}/settings.tsx (93%) delete mode 100644 frontend/src/modules/InplaceVolumetrics/state.ts rename frontend/src/modules/Intersection/{settingsToViewInterface.ts => interfaces.ts} (88%) delete mode 100644 frontend/src/modules/Intersection/state.ts delete mode 100644 frontend/src/modules/Intersection/view/atoms/atomDefinitions.ts create mode 100644 frontend/src/modules/Intersection/view/atoms/baseAtoms.ts create mode 100644 frontend/src/modules/Intersection/view/atoms/derivedAtoms.ts create mode 100644 frontend/src/modules/Intersection/view/atoms/interfaceEffects.ts create mode 100644 frontend/src/modules/Intersection/view/atoms/queryAtoms.ts delete mode 100644 frontend/src/modules/Map/MapState.ts create mode 100644 frontend/src/modules/Map/interfaces.ts create mode 100644 frontend/src/modules/Map/settings/atoms/baseAtoms.ts rename frontend/src/modules/Map/{MapSettings.tsx => settings/settings.tsx} (97%) rename frontend/src/modules/Map/{MapView.tsx => view.tsx} (93%) create mode 100644 frontend/src/modules/MyModule/interfaces.ts create mode 100644 frontend/src/modules/MyModule/settings/atoms/baseAtoms.ts rename frontend/src/modules/MyModule/{ => settings}/settings.tsx (88%) delete mode 100644 frontend/src/modules/MyModule/state.ts create mode 100644 frontend/src/modules/MyModule2/interfaces.ts delete mode 100644 frontend/src/modules/MyModule2/state.ts rename frontend/src/modules/ParameterDistributionMatrix/{settingsToViewInterface.ts => interfaces.ts} (85%) delete mode 100644 frontend/src/modules/ParameterDistributionMatrix/state.ts create mode 100644 frontend/src/modules/Pvt/interfaces.ts delete mode 100644 frontend/src/modules/Pvt/state.ts create mode 100644 frontend/src/modules/Rft/interfaces.ts create mode 100644 frontend/src/modules/Rft/settings/atoms/baseAtoms.ts rename frontend/src/modules/Rft/{state.ts => typesAndEnums.ts} (50%) create mode 100644 frontend/src/modules/SeismicIntersection/interfaces.ts create mode 100644 frontend/src/modules/SeismicIntersection/settings/atoms/baseAtoms.ts create mode 100644 frontend/src/modules/SeismicIntersection/settings/hooks/queryHooks.tsx rename frontend/src/modules/SeismicIntersection/{ => settings}/settings.tsx (94%) delete mode 100644 frontend/src/modules/SeismicIntersection/state.ts delete mode 100644 frontend/src/modules/SeismicIntersection/types.ts create mode 100644 frontend/src/modules/SeismicIntersection/typesAndEnums.ts rename frontend/src/modules/SimulationTimeSeries/{settingsToViewInterface.ts => interfaces.ts} (91%) delete mode 100644 frontend/src/modules/SimulationTimeSeries/settings/atoms/atomDefinitions.ts delete mode 100644 frontend/src/modules/SimulationTimeSeries/state.ts delete mode 100644 frontend/src/modules/SimulationTimeSeries/view/atoms/atomDefinitions.ts create mode 100644 frontend/src/modules/SimulationTimeSeries/view/atoms/baseAtoms.ts create mode 100644 frontend/src/modules/SimulationTimeSeries/view/atoms/derivedAtoms.ts create mode 100644 frontend/src/modules/SimulationTimeSeries/view/atoms/interfaceEffects.ts create mode 100644 frontend/src/modules/SimulationTimeSeries/view/atoms/queryAtoms.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/channelDefs.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/dataGenerators.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/loadModule.tsx delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/preview.svg delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/preview.tsx delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/queryHooks.tsx delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/registerModule.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/settings.tsx delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/state.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/utils/PlotlyTraceUtils/createVectorTracesUtils.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/utils/PlotlyTraceUtils/fanchartPlotting.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/utils/PlotlyTraceUtils/statisticsPlotting.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/utils/colorUtils.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/utils/ensemblesContinuousParameterColoring.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/utils/ensemblesVectorListHelper.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/utils/getContinuousAndNonConstantParameters.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/utils/stringUtils.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/utils/subplotBuilder.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/utils/timeSeriesPlotData.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/utils/vectorSpecificationsAndQueriesUtils.ts delete mode 100644 frontend/src/modules/SimulationTimeSeriesMatrix/view.tsx create mode 100644 frontend/src/modules/SimulationTimeSeriesSensitivity/interfaces.ts create mode 100644 frontend/src/modules/SimulationTimeSeriesSensitivity/settings/atoms/baseAtoms.ts create mode 100644 frontend/src/modules/SimulationTimeSeriesSensitivity/settings/hooks/queryHooks.tsx rename frontend/src/modules/SimulationTimeSeriesSensitivity/{ => settings}/settings.tsx (91%) delete mode 100644 frontend/src/modules/SimulationTimeSeriesSensitivity/state.ts create mode 100644 frontend/src/modules/SimulationTimeSeriesSensitivity/typesAndEnums.ts create mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/interfaces.ts create mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/settings/atoms/baseAtoms.ts rename frontend/src/modules/StructuralUncertaintyIntersection/{ => settings}/components/intersectionSettings.tsx (95%) rename frontend/src/modules/StructuralUncertaintyIntersection/{ => settings}/components/realizationsSelect.tsx (100%) rename frontend/src/modules/StructuralUncertaintyIntersection/{ => settings}/settings.tsx (94%) rename frontend/src/modules/StructuralUncertaintyIntersection/{types.ts => typesAndEnums.ts} (100%) delete mode 100644 frontend/src/modules/SubsurfaceMap/components/AggregationSelector/index.ts create mode 100644 frontend/src/modules/SubsurfaceMap/interfaces.ts create mode 100644 frontend/src/modules/SubsurfaceMap/settings/atoms/baseAtoms.ts rename frontend/src/modules/SubsurfaceMap/{components/AggregationSelector => settings/components}/aggregationSelector.tsx (100%) rename frontend/src/modules/SubsurfaceMap/{ => settings}/settings.tsx (96%) delete mode 100644 frontend/src/modules/SubsurfaceMap/state.ts create mode 100644 frontend/src/modules/TornadoChart/interfaces.ts create mode 100644 frontend/src/modules/TornadoChart/settings/atoms/baseAtoms.ts create mode 100644 frontend/src/modules/TornadoChart/settings/atoms/interfaceEffects.ts rename frontend/src/modules/TornadoChart/{ => settings}/settings.tsx (85%) delete mode 100644 frontend/src/modules/TornadoChart/state.ts create mode 100644 frontend/src/modules/TornadoChart/typesAndEnums.ts create mode 100644 frontend/src/modules/TornadoChart/view/atoms/baseAtoms.ts rename frontend/src/modules/TornadoChart/{ => view/components}/sensitivityChart.tsx (98%) rename frontend/src/modules/TornadoChart/{ => view/components}/sensitivityTable.tsx (95%) rename frontend/src/modules/TornadoChart/{ => view/utils}/sensitivityResponseCalculator.ts (100%) rename frontend/src/modules/TornadoChart/{ => view}/view.tsx (82%) create mode 100644 frontend/src/modules/WellCompletions/interfaces.ts create mode 100644 frontend/src/modules/WellCompletions/settings/atoms/baseAtoms.ts rename frontend/src/modules/WellCompletions/{ => settings/hooks}/queryHooks.tsx (100%) rename frontend/src/modules/WellCompletions/{ => settings}/settings.tsx (96%) create mode 100644 frontend/src/modules/WellCompletions/typesAndEnums.ts rename frontend/tests/unit/{SimulationTimeSeriesMatrixUtils.test.ts => SimulationTimeSeriesUtils.test.ts} (90%) delete mode 100644 frontend/tests/unit/stateStore.test.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1ba835dd7..8a93b7938 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -24,6 +24,7 @@ "culori": "^3.2.0", "geojson": "^0.5.0", "jotai": "^2.6.2", + "jotai-effect": "^1.0.0", "jotai-scope": "^0.5.1", "jotai-tanstack-query": "^0.8.2", "lodash": "^4.17.21", @@ -9592,6 +9593,14 @@ } } }, + "node_modules/jotai-effect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jotai-effect/-/jotai-effect-1.0.0.tgz", + "integrity": "sha512-eCgKKG4BACDzuJGYTu0xZRk1C1MEOvbAhC3L8w7YufQ2lSLORwNX/WFnCuZxLFX0sDLkTUeoUzOYaw8wnXY+UQ==", + "peerDependencies": { + "jotai": ">=2.5.0" + } + }, "node_modules/jotai-scope": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/jotai-scope/-/jotai-scope-0.5.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 4f160991f..9225d5e0d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -35,6 +35,7 @@ "culori": "^3.2.0", "geojson": "^0.5.0", "jotai": "^2.6.2", + "jotai-effect": "^1.0.0", "jotai-scope": "^0.5.1", "jotai-tanstack-query": "^0.8.2", "lodash": "^4.17.21", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 2f4c8e0b1..1d03a1c5b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -44,45 +44,50 @@ enum InitAppState { const layout: LayoutElement[] = []; -const WORKBENCH = new Workbench(); - function App() { + // Workbench must be kept as a state in order to keep it when any framework code is changed in dev mode. + // Otherwise, the workbench will be reset on every code change. This would cause it to loose its state and will + // cause the app to crash. + const [workbench] = React.useState(new Workbench()); + const [isMounted, setIsMounted] = React.useState(false); const [initAppState, setInitAppState] = React.useState(InitAppState.CheckingIfUserIsSignedIn); const queryClient = useQueryClient(); const { authState } = useAuthProvider(); - function initApp() { - if (!WORKBENCH.loadLayoutFromLocalStorage()) { - WORKBENCH.makeLayout(layout); - } - - if (WORKBENCH.getLayout().length === 0) { - WORKBENCH.getGuiMessageBroker().setState(GuiState.LeftDrawerContent, LeftDrawerContent.ModulesList); - } else { - WORKBENCH.getGuiMessageBroker().setState(GuiState.LeftDrawerContent, LeftDrawerContent.ModuleSettings); - } - setInitAppState(InitAppState.InitCompleted); - WORKBENCH.getGuiMessageBroker().setState(GuiState.AppInitialized, true); - } - function signIn() { window.location.href = `/api/login?redirect_url_after_login=${btoa("/")}`; } React.useEffect( function handleMountWhenSignedIn() { + function initApp() { + if (!workbench.loadLayoutFromLocalStorage()) { + workbench.makeLayout(layout); + } + + if (workbench.getLayout().length === 0) { + workbench.getGuiMessageBroker().setState(GuiState.LeftDrawerContent, LeftDrawerContent.ModulesList); + } else { + workbench + .getGuiMessageBroker() + .setState(GuiState.LeftDrawerContent, LeftDrawerContent.ModuleSettings); + } + setInitAppState(InitAppState.InitCompleted); + workbench.getGuiMessageBroker().setState(GuiState.AppInitialized, true); + } + if (authState !== AuthState.LoggedIn || isMounted) { return; } setIsMounted(true); - const storedEnsembleIdents = WORKBENCH.maybeLoadEnsembleSettingsFromLocalStorage(); + const storedEnsembleIdents = workbench.maybeLoadEnsembleSettingsFromLocalStorage(); if (storedEnsembleIdents) { setInitAppState(InitAppState.LoadingEnsembles); - WORKBENCH.loadAndSetupEnsembleSetInSession(queryClient, storedEnsembleIdents).finally(() => { + workbench.loadAndSetupEnsembleSetInSession(queryClient, storedEnsembleIdents).finally(() => { initApp(); }); } else { @@ -90,11 +95,11 @@ function App() { } return function handleUnmount() { - WORKBENCH.clearLayout(); - WORKBENCH.resetModuleInstanceNumbers(); + workbench.clearLayout(); + workbench.resetModuleInstanceNumbers(); }; }, - [authState, isMounted, queryClient] + [authState, isMounted, queryClient, workbench] ); function makeStateMessages() { @@ -160,12 +165,12 @@ function App() { })} > <> - - - + + +
- + ); } diff --git a/frontend/src/framework/Module.tsx b/frontend/src/framework/Module.tsx index 6247bb0ed..dc81aaee0 100644 --- a/frontend/src/framework/Module.tsx +++ b/frontend/src/framework/Module.tsx @@ -1,7 +1,6 @@ import React from "react"; -import { Atom, PrimitiveAtom, WritableAtom } from "jotai"; -import { cloneDeep } from "lodash"; +import { Getter, Setter } from "jotai"; import { ChannelDefinition, ChannelReceiverDefinition } from "./DataChannelTypes"; import { InitialSettings } from "./InitialSettings"; @@ -9,13 +8,8 @@ import { SettingsContext, ViewContext } from "./ModuleContext"; import { ModuleDataTagId } from "./ModuleDataTags"; import { ModuleInstance, ModuleInstanceTopic } from "./ModuleInstance"; import { DrawPreviewFunc } from "./Preview"; -import { StateBaseType, StateOptions } from "./StateStore"; import { SyncSettingKey } from "./SyncSettings"; -import { - InterfaceBaseType, - InterfaceInitialization, - UniDirectionalModuleComponentsInterface, -} from "./UniDirectionalModuleComponentsInterface"; +import { InterfaceBaseType, InterfaceInitialization } from "./UniDirectionalModuleComponentsInterface"; import { Workbench } from "./Workbench"; import { WorkbenchServices } from "./WorkbenchServices"; import { WorkbenchSession } from "./WorkbenchSession"; @@ -33,13 +27,27 @@ export enum ModuleDevState { DEPRECATED = "deprecated", } +export type ModuleInterfaceTypes = { + settingsToView?: InterfaceBaseType; + viewToSettings?: InterfaceBaseType; +}; + +export type ModuleInterfacesInitializations = { + settingsToView: TInterfaceTypes["settingsToView"] extends undefined + ? undefined + : InterfaceInitialization>; + viewToSettings: TInterfaceTypes["viewToSettings"] extends undefined + ? undefined + : InterfaceInitialization>; +}; + export type ModuleSettingsProps< - TTStateType extends StateBaseType, - TInterfaceType extends InterfaceBaseType = Record, - TSettingsAtomsType extends Record = Record, - TViewAtomsType extends Record = Record + TInterfaceTypes extends ModuleInterfaceTypes = { + settingsToView: Record; + viewToSettings: Record; + } > = { - settingsContext: SettingsContext; + settingsContext: SettingsContext; workbenchSession: WorkbenchSession; workbenchServices: WorkbenchServices; workbenchSettings: WorkbenchSettings; @@ -47,39 +55,37 @@ export type ModuleSettingsProps< }; export type ModuleViewProps< - TTStateType extends StateBaseType, - TInterfaceType extends InterfaceBaseType = Record, - TSettingsAtomsType extends Record = Record, - TViewAtomsType extends Record = Record + TInterfaceTypes extends ModuleInterfaceTypes = { + settingsToView: Record; + viewToSettings: Record; + } > = { - viewContext: ViewContext; + viewContext: ViewContext; workbenchSession: WorkbenchSession; workbenchServices: WorkbenchServices; workbenchSettings: WorkbenchSettings; initialSettings?: InitialSettings; }; -export type ModuleAtoms> = { - [K in keyof TAtoms]: Atom | WritableAtom | PrimitiveAtom; -}; - -export type AtomsInitialization, TInterfaceType extends InterfaceBaseType> = ( - settingsToViewInterface: UniDirectionalModuleComponentsInterface -) => ModuleAtoms; +export type InterfaceEffects = (( + getInterfaceValue: (key: TKey) => TInterfaceType[TKey], + setAtomValue: Setter, + getAtomValue: Getter +) => void)[]; export type ModuleSettings< - TTStateType extends StateBaseType, - TInterfaceType extends InterfaceBaseType = Record, - TSettingsAtomsType extends Record = Record, - TViewAtomsType extends Record = Record -> = React.FC>; + TInterfaceTypes extends ModuleInterfaceTypes = { + settingsToView: Record; + viewToSettings: Record; + } +> = React.FC>; export type ModuleView< - TTStateType extends StateBaseType, - TInterfaceType extends InterfaceBaseType = Record, - TSettingsAtomsType extends Record = Record, - TViewAtomsType extends Record = Record -> = React.FC>; + TInterfaceTypes extends ModuleInterfaceTypes = { + settingsToView: Record; + viewToSettings: Record; + } +> = React.FC>; export enum ImportState { NotImported = "NotImported", @@ -101,24 +107,24 @@ export interface ModuleOptions { channelReceiverDefinitions?: ChannelReceiverDefinition[]; } -export class Module< - TStateType extends StateBaseType, - TInterfaceType extends InterfaceBaseType, - TSettingsAtomsType extends Record, - TViewAtomsType extends Record -> { +export class Module { private _name: string; private _defaultTitle: string; - public viewFC: ModuleView; - public settingsFC: ModuleSettings; - protected _importState: ImportState; - private _moduleInstances: ModuleInstance[]; - private _defaultState: TStateType | null; - private _settingsToViewInterfaceInitialization: InterfaceInitialization | null; - private _settingsAtomsInitialization: AtomsInitialization | null; - private _viewAtomsInitialization: AtomsInitialization | null; - private _stateOptions: StateOptions | undefined; - private _workbench: Workbench | null; + public viewFC: ModuleView; + public settingsFC: ModuleSettings; + protected _importState: ImportState = ImportState.NotImported; + private _moduleInstances: ModuleInstance[] = []; + private _settingsToViewInterfaceInitialization: InterfaceInitialization< + Exclude + > | null = null; + private _viewToSettingsInterfaceInitialization: InterfaceInitialization< + Exclude + > | null = null; + private _viewToSettingsInterfaceEffects: InterfaceEffects> = + []; + private _settingsToViewInterfaceEffects: InterfaceEffects> = + []; + private _workbench: Workbench | null = null; private _syncableSettingKeys: SyncSettingKey[]; private _drawPreviewFunc: DrawPreviewFunc | null; private _description: string | null; @@ -135,13 +141,6 @@ export class Module< this._devState = options.devState; this.viewFC = () =>
Not defined
; this.settingsFC = () =>
Not defined
; - this._importState = ImportState.NotImported; - this._moduleInstances = []; - this._defaultState = null; - this._settingsToViewInterfaceInitialization = null; - this._settingsAtomsInitialization = null; - this._viewAtomsInitialization = null; - this._workbench = null; this._syncableSettingKeys = options.syncableSettingKeys ?? []; this._drawPreviewFunc = options.drawPreviewFunc ?? null; this._description = options.description ?? null; @@ -186,26 +185,36 @@ export class Module< this._workbench = workbench; } - setDefaultState(defaultState: TStateType, options?: StateOptions): void { - this._defaultState = defaultState; - this._stateOptions = options; - this._moduleInstances.forEach((instance) => { - if (this._defaultState && !instance.isInitialized()) { - instance.setDefaultState(cloneDeep(this._defaultState), cloneDeep(this._stateOptions)); - } - }); + setSettingsToViewInterfaceInitialization( + interfaceInitialization: InterfaceInitialization> + ): void { + this._settingsToViewInterfaceInitialization = interfaceInitialization; } - setSettingsToViewInterfaceInitialization(interfaceInitialization: InterfaceInitialization): void { - this._settingsToViewInterfaceInitialization = interfaceInitialization; + setViewToSettingsInterfaceInitialization( + interfaceInitialization: InterfaceInitialization> + ): void { + this._viewToSettingsInterfaceInitialization = interfaceInitialization; + } + + setViewToSettingsInterfaceEffects( + atomsInitialization: InterfaceEffects> + ): void { + this._viewToSettingsInterfaceEffects = atomsInitialization; } - setSettingsAtomsInitialization(atomsInitialization: AtomsInitialization): void { - this._settingsAtomsInitialization = atomsInitialization; + setSettingsToViewInterfaceEffects( + atomsInitialization: InterfaceEffects> + ): void { + this._settingsToViewInterfaceEffects = atomsInitialization; } - setViewAtomsInitialization(atomsInitialization: AtomsInitialization): void { - this._viewAtomsInitialization = atomsInitialization; + getViewToSettingsInterfaceEffects(): InterfaceEffects> { + return this._viewToSettingsInterfaceEffects; + } + + getSettingsToViewInterfaceEffects(): InterfaceEffects> { + return this._settingsToViewInterfaceEffects; } getSyncableSettingKeys(): SyncSettingKey[] { @@ -216,14 +225,12 @@ export class Module< return this._syncableSettingKeys.includes(key); } - makeInstance( - instanceNumber: number - ): ModuleInstance { + makeInstance(instanceNumber: number): ModuleInstance { if (!this._workbench) { throw new Error("Module must be added to a workbench before making an instance"); } - const instance = new ModuleInstance({ + const instance = new ModuleInstance({ module: this, workbench: this._workbench, instanceNumber, @@ -246,25 +253,26 @@ export class Module< } } + private initializeModuleInstance(instance: ModuleInstance): void { + instance.initialize(); + if (this._settingsToViewInterfaceInitialization) { + instance.makeSettingsToViewInterface(this._settingsToViewInterfaceInitialization); + } + instance.makeSettingsToViewInterfaceEffectsAtom(); + if (this._viewToSettingsInterfaceInitialization) { + instance.makeViewToSettingsInterface(this._viewToSettingsInterfaceInitialization); + } + instance.makeViewToSettingsInterfaceEffectsAtom(); + } + private maybeImportSelf(): void { if (this._importState !== ImportState.NotImported) { - if (this._defaultState && this._importState === ImportState.Imported) { + if (this._importState === ImportState.Imported) { this._moduleInstances.forEach((instance) => { if (instance.isInitialized()) { return; } - if (this._defaultState) { - instance.setDefaultState(cloneDeep(this._defaultState), cloneDeep(this._stateOptions)); - } - if (this._settingsToViewInterfaceInitialization) { - instance.makeSettingsToViewInterface(this._settingsToViewInterfaceInitialization); - if (this._settingsAtomsInitialization) { - instance.makeSettingsAtoms(this._settingsAtomsInitialization); - } - if (this._viewAtomsInitialization) { - instance.makeViewAtoms(this._viewAtomsInitialization); - } - } + this.initializeModuleInstance(instance); }); } return; @@ -276,18 +284,7 @@ export class Module< .then(() => { this.setImportState(ImportState.Imported); this._moduleInstances.forEach((instance) => { - if (this._defaultState) { - instance.setDefaultState(cloneDeep(this._defaultState), cloneDeep(this._stateOptions)); - } - if (this._settingsToViewInterfaceInitialization) { - instance.makeSettingsToViewInterface(this._settingsToViewInterfaceInitialization); - if (this._settingsAtomsInitialization) { - instance.makeSettingsAtoms(this._settingsAtomsInitialization); - } - if (this._viewAtomsInitialization) { - instance.makeViewAtoms(this._viewAtomsInitialization); - } - } + this.initializeModuleInstance(instance); }); }) .catch((e) => { diff --git a/frontend/src/framework/ModuleContext.ts b/frontend/src/framework/ModuleContext.ts index 38ac9383d..7bb1a9b89 100644 --- a/frontend/src/framework/ModuleContext.ts +++ b/frontend/src/framework/ModuleContext.ts @@ -10,9 +10,8 @@ */ /* eslint-disable react-hooks/rules-of-hooks */ -import { WritableAtom, useAtom, useAtomValue, useSetAtom } from "jotai"; - import { ChannelContentDefinition, KeyKind } from "./DataChannelTypes"; +import { ModuleInterfaceTypes } from "./Module"; import { ModuleInstance, ModuleInstanceTopic, @@ -20,53 +19,22 @@ import { useModuleInstanceTopicValue, } from "./ModuleInstance"; import { ModuleInstanceStatusController } from "./ModuleInstanceStatusController"; -import { StateBaseType, StateStore, useSetStoreValue, useStoreState, useStoreValue } from "./StateStore"; import { SyncSettingKey } from "./SyncSettings"; -import { InterfaceBaseType, useSettingsToViewInterfaceValue } from "./UniDirectionalModuleComponentsInterface"; +import { InterfaceBaseType, useInterfaceValue } from "./UniDirectionalModuleComponentsInterface"; import { useChannelReceiver } from "./internal/DataChannels/hooks/useChannelReceiver"; import { usePublishChannelContents } from "./internal/DataChannels/hooks/usePublishChannelContents"; -export class ModuleContext< - TStateType extends StateBaseType, - TInterfaceType extends InterfaceBaseType, - TSettingsAtomsType extends Record, - TViewAtomsType extends Record -> { - protected _moduleInstance: ModuleInstance; - private _stateStore: StateStore; - - constructor( - moduleInstance: ModuleInstance, - stateStore: StateStore - ) { +export class ModuleContext { + protected _moduleInstance: ModuleInstance; + + constructor(moduleInstance: ModuleInstance) { this._moduleInstance = moduleInstance; - this._stateStore = stateStore; } getInstanceIdString(): string { return this._moduleInstance.getId(); } - getStateStore(): StateStore { - return this._stateStore; - } - - useStoreState( - key: K - ): [TStateType[K], (value: TStateType[K] | ((prev: TStateType[K]) => TStateType[K])) => void] { - return useStoreState(this._stateStore, key); - } - - useStoreValue(key: K): TStateType[K] { - return useStoreValue(this._stateStore, key); - } - - useSetStoreValue( - key: K - ): (newValue: TStateType[K] | ((prev: TStateType[K]) => TStateType[K])) => void { - return useSetStoreValue(this._stateStore, key); - } - useModuleInstanceTopic(topic: T): ModuleInstanceTopicValueTypes[T] { return useModuleInstanceTopicValue(this._moduleInstance, topic); } @@ -114,86 +82,25 @@ export class ModuleContext< }); } - useSettingsToViewInterfaceValue(key: TKey): TInterfaceType[TKey]; - useSettingsToViewInterfaceValue(key: TKey): TInterfaceType[TKey] { - return useSettingsToViewInterfaceValue(this._moduleInstance.getUniDirectionalSettingsToViewInterface(), key); - } - - useViewAtom( + useSettingsToViewInterfaceValue( key: TKey - ): [Awaited, (value: TViewAtomsType[TKey]) => void] { - const atom = this._moduleInstance.getViewAtom(key); - - return useAtom(atom); - } - - useViewAtomValue(key: TKey): TViewAtomsType[TKey] { - const atom = this._moduleInstance.getViewAtom(key); - - return useAtomValue(atom); - } - - useSetViewAtom< - TKey extends keyof Pick< - TViewAtomsType, - keyof { - [key in keyof TViewAtomsType]: TViewAtomsType[key] extends WritableAtom ? key : never; - } - > - >(key: TKey): (...args: [TViewAtomsType[TKey]]) => void { - const atom = this._moduleInstance.getViewAtom(key) as WritableAtom; - return useSetAtom(atom); + ): TInterfaceTypes["settingsToView"][TKey] { + return useInterfaceValue(this._moduleInstance.getUniDirectionalSettingsToViewInterface(), key); } - useSettingsAtom( + useViewToSettingsInterfaceValue( key: TKey - ): [Awaited, (value: TSettingsAtomsType[TKey]) => void] { - const atom = this._moduleInstance.getSettingsAtom(key); - - return useAtom(atom); - } - - useSettingsAtomValue(key: TKey): TSettingsAtomsType[TKey] { - const atom = this._moduleInstance.getSettingsAtom(key); - - return useAtomValue(atom); - } - - useSetSettingsAtom< - TKey extends keyof Pick< - TSettingsAtomsType, - keyof { - [key in keyof TSettingsAtomsType]: TSettingsAtomsType[key] extends WritableAtom - ? key - : never; - } - > - >(key: TKey): (...args: [TSettingsAtomsType[TKey]]) => void { - const atom = this._moduleInstance.getSettingsAtom(key) as WritableAtom; - return useSetAtom(atom); + ): TInterfaceTypes["viewToSettings"][TKey] { + return useInterfaceValue(this._moduleInstance.getUniDirectionalViewToSettingsInterface(), key); } } -export type ViewContext< - StateType extends StateBaseType, - TInterfaceType extends InterfaceBaseType, - TSettingsAtomsType extends Record, - TViewAtomsType extends Record -> = Omit< - ModuleContext, - | "useSettingsToViewInterfaceState" - | "useSetSettingsToViewInterfaceValue" - | "useSettingsAtom" - | "useSetSettingsAtom" - | "useSettingsAtomValue" +export type ViewContext = Omit< + ModuleContext, + "useViewToSettingsInterfaceValue" | "useSettingsAtom" | "useSetSettingsAtom" | "useSettingsAtomValue" >; -export type SettingsContext< - StateType extends StateBaseType, - TInterfaceType extends InterfaceBaseType, - TSettingsAtomsType extends Record, - TViewAtomsType extends Record -> = Omit< - ModuleContext, - "useViewAtom" | "useViewAtomValue" | "useSetViewAtom" +export type SettingsContext = Omit< + ModuleContext, + "useSettingsToViewInterfaceValue" | "useViewAtom" | "useViewAtomValue" | "useSetViewAtom" >; diff --git a/frontend/src/framework/ModuleInstance.ts b/frontend/src/framework/ModuleInstance.ts index 12767d24b..968e56d2f 100644 --- a/frontend/src/framework/ModuleInstance.ts +++ b/frontend/src/framework/ModuleInstance.ts @@ -1,16 +1,14 @@ import React, { ErrorInfo } from "react"; -import { cloneDeep } from "lodash"; +import { Atom, atom } from "jotai"; +import { atomEffect } from "jotai-effect"; -import { AtomStore } from "./AtomStoreMaster"; import { ChannelDefinition, ChannelReceiverDefinition } from "./DataChannelTypes"; import { InitialSettings } from "./InitialSettings"; -import { AtomsInitialization, ImportState, Module, ModuleAtoms, ModuleSettings, ModuleView } from "./Module"; +import { ImportState, Module, ModuleInterfaceTypes, ModuleSettings, ModuleView } from "./Module"; import { ModuleContext } from "./ModuleContext"; -import { StateBaseType, StateOptions, StateStore } from "./StateStore"; import { SyncSettingKey } from "./SyncSettings"; import { - InterfaceBaseType, InterfaceInitialization, UniDirectionalModuleComponentsInterface, } from "./UniDirectionalModuleComponentsInterface"; @@ -39,62 +37,40 @@ export type ModuleInstanceTopicValueTypes = { [ModuleInstanceTopic.IMPORT_STATE]: ImportState; }; -export interface ModuleInstanceOptions< - TStateType extends StateBaseType, - TInterfaceType extends InterfaceBaseType, - TSettingsAtomsType extends Record, - TViewAtomsType extends Record -> { - module: Module; +export interface ModuleInstanceOptions { + module: Module; workbench: Workbench; instanceNumber: number; channelDefinitions: ChannelDefinition[] | null; channelReceiverDefinitions: ChannelReceiverDefinition[] | null; } -export class ModuleInstance< - TStateType extends StateBaseType, - TInterfaceType extends InterfaceBaseType, - TSettingsAtomsType extends Record, - TViewAtomsType extends Record -> { +export class ModuleInstance { private _id: string; private _title: string; - private _initialised: boolean; - private _moduleInstanceState: ModuleInstanceState; - private _fatalError: { err: Error; errInfo: ErrorInfo } | null; - private _syncedSettingKeys: SyncSettingKey[]; - private _stateStore: StateStore | null; - private _module: Module; - private _context: ModuleContext | null; + private _initialized: boolean = false; + private _moduleInstanceState: ModuleInstanceState = ModuleInstanceState.INITIALIZING; + private _fatalError: { err: Error; errInfo: ErrorInfo } | null = null; + private _syncedSettingKeys: SyncSettingKey[] = []; + private _module: Module; + private _context: ModuleContext | null = null; private _subscribers: Map void>> = new Map(); - private _cachedDefaultState: TStateType | null; - private _cachedStateStoreOptions?: StateOptions; - private _initialSettings: InitialSettings | null; - private _statusController: ModuleInstanceStatusControllerInternal; + private _initialSettings: InitialSettings | null = null; + private _statusController: ModuleInstanceStatusControllerInternal = new ModuleInstanceStatusControllerInternal(); private _channelManager: ChannelManager; - private _workbench: Workbench; - private _settingsViewInterface: UniDirectionalModuleComponentsInterface | null; - private _settingsAtoms: ModuleAtoms | null; - private _viewAtoms: ModuleAtoms | null; - - constructor(options: ModuleInstanceOptions) { + private _settingsToViewInterface: UniDirectionalModuleComponentsInterface< + Exclude + > | null = null; + private _viewToSettingsInterface: UniDirectionalModuleComponentsInterface< + Exclude + > | null = null; + private _settingsToViewInterfaceEffectsAtom: Atom | null = null; + private _viewToSettingsInterfaceEffectsAtom: Atom | null = null; + + constructor(options: ModuleInstanceOptions) { this._id = `${options.module.getName()}-${options.instanceNumber}`; this._title = options.module.getDefaultTitle(); - this._stateStore = null; this._module = options.module; - this._context = null; - this._initialised = false; - this._syncedSettingKeys = []; - this._moduleInstanceState = ModuleInstanceState.INITIALIZING; - this._fatalError = null; - this._cachedDefaultState = null; - this._initialSettings = null; - this._statusController = new ModuleInstanceStatusControllerInternal(); - this._workbench = options.workbench; - this._settingsViewInterface = null; - this._settingsAtoms = null; - this._viewAtoms = null; this._channelManager = new ChannelManager(this._id); @@ -112,66 +88,114 @@ export class ModuleInstance< } } - getAtomStore(): AtomStore { - return this._workbench.getAtomStoreMaster().getAtomStoreForModuleInstance(this._id); - } - - getUniDirectionalSettingsToViewInterface(): UniDirectionalModuleComponentsInterface { - if (!this._settingsViewInterface) { + getUniDirectionalSettingsToViewInterface(): UniDirectionalModuleComponentsInterface< + Exclude + > { + if (!this._settingsToViewInterface) { throw `Module instance '${this._title}' does not have an interface yet. Did you forget to init the module?`; } - return this._settingsViewInterface; - } - - getSettingsAtom(key: TKey): ModuleAtoms[TKey] { - if (!this._settingsAtoms) { - throw `Module instance '${this._title}' does not have initialized settings atoms yet. Did you forget to add an atom initialization when registering the module?`; - } - return this._settingsAtoms[key]; + return this._settingsToViewInterface; } - getViewAtom(key: TKey): ModuleAtoms[TKey] { - if (!this._viewAtoms) { - throw `Module instance '${this._title}' does not have initialized view atoms yet. Did you forget to add an atom initialization when registering the module?`; + getUniDirectionalViewToSettingsInterface(): UniDirectionalModuleComponentsInterface< + Exclude + > { + if (!this._viewToSettingsInterface) { + throw `Module instance '${this._title}' does not have an interface yet. Did you forget to init the module?`; } - return this._viewAtoms[key]; + return this._viewToSettingsInterface; } getChannelManager(): ChannelManager { return this._channelManager; } - setDefaultState(defaultState: TStateType, options?: StateOptions): void { - if (this._cachedDefaultState === null) { - this._cachedDefaultState = defaultState; - this._cachedStateStoreOptions = options; + initialize(): void { + this._context = new ModuleContext(this); + this._initialized = true; + this.setModuleInstanceState(ModuleInstanceState.OK); + } + + makeSettingsToViewInterface( + interfaceInitialization: InterfaceInitialization> + ) { + if (!interfaceInitialization) { + return; } + this._settingsToViewInterface = new UniDirectionalModuleComponentsInterface(interfaceInitialization); + } - this._stateStore = new StateStore(cloneDeep(defaultState), options); - this._context = new ModuleContext( - this, - this._stateStore - ); - this._initialised = true; - this.setModuleInstanceState(ModuleInstanceState.OK); + makeViewToSettingsInterface( + interfaceInitialization: InterfaceInitialization> + ) { + if (!interfaceInitialization) { + return; + } + this._viewToSettingsInterface = new UniDirectionalModuleComponentsInterface(interfaceInitialization); + } + + makeSettingsToViewInterfaceEffectsAtom(): void { + const effectFuncs = this.getModule().getSettingsToViewInterfaceEffects(); + const getUniDirectionalSettingsToViewInterface: () => UniDirectionalModuleComponentsInterface< + Exclude + > = () => this.getUniDirectionalSettingsToViewInterface(); + + const newEffects: Atom[] = []; + for (const effectFunc of effectFuncs) { + const effect = atomEffect((get, set) => { + function getAtomFromInterface>( + key: TKey + ): Exclude[TKey] { + return get(getUniDirectionalSettingsToViewInterface().getAtom(key)); + } + effectFunc(getAtomFromInterface, set, get); + }); + newEffects.push(effect); + } + this._settingsToViewInterfaceEffectsAtom = atom((get) => { + for (const effect of newEffects) { + get(effect); + } + }); } - makeSettingsToViewInterface(interfaceInitialization: InterfaceInitialization) { - this._settingsViewInterface = new UniDirectionalModuleComponentsInterface(interfaceInitialization); + makeViewToSettingsInterfaceEffectsAtom(): void { + const effectFuncs = this.getModule().getViewToSettingsInterfaceEffects(); + const getUniDirectionalViewToSettingsInterface: () => UniDirectionalModuleComponentsInterface< + Exclude + > = () => this.getUniDirectionalViewToSettingsInterface(); + + const newEffects: Atom[] = []; + for (const effectFunc of effectFuncs) { + const effect = atomEffect((get, set) => { + function getAtomFromInterface>( + key: TKey + ): Exclude[TKey] { + return get(getUniDirectionalViewToSettingsInterface().getAtom(key)); + } + effectFunc(getAtomFromInterface, set, get); + }); + newEffects.push(effect); + } + this._viewToSettingsInterfaceEffectsAtom = atom((get) => { + for (const effect of newEffects) { + get(effect); + } + }); } - makeSettingsAtoms(initFunc: AtomsInitialization) { - if (!this._settingsViewInterface) { - throw `Module instance '${this._title}' does not have an interface yet. Did you forget to init the module?`; + getSettingsToViewInterfaceEffectsAtom(): Atom { + if (!this._settingsToViewInterfaceEffectsAtom) { + throw `Module instance '${this._title}' does not have settings to view interface effects yet. Did you forget to init the module?`; } - this._settingsAtoms = initFunc(this._settingsViewInterface); + return this._settingsToViewInterfaceEffectsAtom; } - makeViewAtoms(initFunc: AtomsInitialization) { - if (!this._settingsViewInterface) { - throw `Module instance '${this._title}' does not have an interface yet. Did you forget to init the module?`; + getViewToSettingsInterfaceEffectsAtom(): Atom { + if (!this._viewToSettingsInterfaceEffectsAtom) { + throw `Module instance '${this._title}' does not have view to settings interface effects yet. Did you forget to init the module?`; } - this._viewAtoms = initFunc(this._settingsViewInterface); + return this._viewToSettingsInterfaceEffectsAtom; } addSyncedSetting(settingKey: SyncSettingKey): void { @@ -193,14 +217,14 @@ export class ModuleInstance< } isInitialized(): boolean { - return this._initialised; + return this._initialized; } - getViewFC(): ModuleView { + getViewFC(): ModuleView { return this._module.viewFC; } - getSettingsFC(): ModuleSettings { + getSettingsFC(): ModuleSettings { return this._module.settingsFC; } @@ -208,7 +232,7 @@ export class ModuleInstance< return this._module.getImportState(); } - getContext(): ModuleContext { + getContext(): ModuleContext { if (!this._context) { throw `Module context is not available yet. Did you forget to init the module '${this._title}.'?`; } @@ -275,7 +299,7 @@ export class ModuleInstance< return snapshotGetter; } - getModule(): Module { + getModule(): Module { return this._module; } @@ -311,7 +335,7 @@ export class ModuleInstance< this.setModuleInstanceState(ModuleInstanceState.RESETTING); return new Promise((resolve) => { - this.setDefaultState(this._cachedDefaultState as TStateType, this._cachedStateStoreOptions); + this.initialize(); resolve(); }); } @@ -326,7 +350,7 @@ export class ModuleInstance< } export function useModuleInstanceTopicValue( - moduleInstance: ModuleInstance, + moduleInstance: ModuleInstance, topic: T ): ModuleInstanceTopicValueTypes[T] { const value = React.useSyncExternalStore( diff --git a/frontend/src/framework/ModuleRegistry.ts b/frontend/src/framework/ModuleRegistry.ts index 898cce124..c6fdc7766 100644 --- a/frontend/src/framework/ModuleRegistry.ts +++ b/frontend/src/framework/ModuleRegistry.ts @@ -1,10 +1,9 @@ import { ChannelDefinition, ChannelReceiverDefinition } from "./DataChannelTypes"; -import { AtomsInitialization, Module, ModuleCategory, ModuleDevState } from "./Module"; +import { InterfaceEffects, Module, ModuleCategory, ModuleDevState, ModuleInterfaceTypes } from "./Module"; import { ModuleDataTagId } from "./ModuleDataTags"; import { DrawPreviewFunc } from "./Preview"; -import { StateBaseType, StateOptions } from "./StateStore"; import { SyncSettingKey } from "./SyncSettings"; -import { InterfaceBaseType, InterfaceInitialization } from "./UniDirectionalModuleComponentsInterface"; +import { InterfaceInitialization } from "./UniDirectionalModuleComponentsInterface"; import { ModuleNotFoundPlaceholder } from "./internal/ModuleNotFoundPlaceholder"; export type RegisterModuleOptions = { @@ -31,19 +30,16 @@ export class ModuleNotFoundError extends Error { } export class ModuleRegistry { - private static _registeredModules: Record> = {}; - private static _moduleNotFoundPlaceholders: Record> = {}; + private static _registeredModules: Record> = {}; + private static _moduleNotFoundPlaceholders: Record> = {}; /* eslint-disable-next-line @typescript-eslint/no-empty-function */ private constructor() {} - static registerModule< - TStateType extends StateBaseType, - TInterfaceType extends InterfaceBaseType = Record, - TSettingsAtomsType extends Record = Record, - TViewAtomsType extends Record = Record - >(options: RegisterModuleOptions): Module { - const module = new Module({ + static registerModule( + options: RegisterModuleOptions + ): Module { + const module = new Module({ name: options.moduleName, defaultTitle: options.defaultTitle, category: options.category, @@ -59,50 +55,52 @@ export class ModuleRegistry { return module; } - static initModule< - TStateType extends StateBaseType, - TInterfaceType extends InterfaceBaseType = Record, - TSettingsAtomsType extends Record = Record, - TViewAtomsType extends Record = Record - >( + static initModule( moduleName: string, - defaultState: TStateType, - options?: StateOptions, - interfaceInitialization?: InterfaceInitialization, - settingsAtomsInitialization?: AtomsInitialization, - viewAtomsInitialization?: AtomsInitialization - ): Module { + options: { + settingsToViewInterfaceInitialization?: TInterfaceTypes["settingsToView"] extends undefined + ? undefined + : InterfaceInitialization>; + viewToSettingsInterfaceInitialization?: TInterfaceTypes["viewToSettings"] extends undefined + ? undefined + : InterfaceInitialization>; + viewToSettingsInterfaceEffects?: InterfaceEffects>; + settingsToViewInterfaceEffects?: InterfaceEffects>; + } + ): Module { const module = this._registeredModules[moduleName]; if (module) { - module.setDefaultState(defaultState, options); - if (interfaceInitialization) { - module.setSettingsToViewInterfaceInitialization(interfaceInitialization); + if (options.settingsToViewInterfaceInitialization) { + module.setSettingsToViewInterfaceInitialization(options.settingsToViewInterfaceInitialization); + } + if (options.viewToSettingsInterfaceInitialization) { + module.setViewToSettingsInterfaceInitialization(options.viewToSettingsInterfaceInitialization); } - if (settingsAtomsInitialization) { - module.setSettingsAtomsInitialization(settingsAtomsInitialization); + if (options.viewToSettingsInterfaceEffects) { + module.setViewToSettingsInterfaceEffects(options.viewToSettingsInterfaceEffects); } - if (viewAtomsInitialization) { - module.setViewAtomsInitialization(viewAtomsInitialization); + if (options.settingsToViewInterfaceEffects) { + module.setSettingsToViewInterfaceEffects(options.settingsToViewInterfaceEffects); } - return module as Module; + return module as Module; } throw new ModuleNotFoundError(moduleName); } - static getModule(moduleName: string): Module { + static getModule(moduleName: string): Module { const module = this._registeredModules[moduleName]; if (module) { - return module as Module; + return module as Module; } const placeholder = this._moduleNotFoundPlaceholders[moduleName]; if (placeholder) { - return placeholder as Module; + return placeholder as Module; } this._moduleNotFoundPlaceholders[moduleName] = new ModuleNotFoundPlaceholder(moduleName); - return this._moduleNotFoundPlaceholders[moduleName] as Module; + return this._moduleNotFoundPlaceholders[moduleName] as Module; } - static getRegisteredModules(): Record> { + static getRegisteredModules(): Record> { return this._registeredModules; } } diff --git a/frontend/src/framework/StateStore.ts b/frontend/src/framework/StateStore.ts deleted file mode 100644 index f6ebfce30..000000000 --- a/frontend/src/framework/StateStore.ts +++ /dev/null @@ -1,104 +0,0 @@ -import React from "react"; - -import { isEqual } from "lodash"; - -export type StateBaseType = object; -export type StateOptions = { - [K in keyof T]?: { - deepCompare?: boolean; - }; -}; - -export class StateStore { - private _state: Record; - private _options?: StateOptions; - private _subscribersMap: Partial>>; - - constructor(defaultState: StateType, options?: StateOptions) { - this._state = defaultState; - this._subscribersMap = {}; - this._options = options; - } - - hasKey(key: keyof StateType): boolean { - return key in this._state; - } - - getValue(key: K): StateType[K] { - return this._state[key]; - } - - setValue(key: K, value: StateType[K]) { - if (this._state[key] === value) { - return; - } - - if (this._options && this._options[key]?.deepCompare) { - if (isEqual(value, this._state[key])) { - return; - } - } - - this._state[key] = value; - const subscribersSet = this._subscribersMap[key] || new Set(); - for (const cb of subscribersSet) { - cb(value); - } - } - - subscribe(key: K, cb: (value: StateType[K]) => void) { - const subscribersSet = this._subscribersMap[key] || new Set(); - subscribersSet.add(cb); - this._subscribersMap[key] = subscribersSet; - - // Trigger the callback immediately in case we have some late subscribers - cb(this._state[key]); - - return () => { - subscribersSet.delete(cb); - }; - } -} - -export function useStoreState( - stateStore: StateStore, - key: T -): [S[T], (value: S[T] | ((prev: S[T]) => S[T])) => void] { - const [state, setState] = React.useState(stateStore.getValue(key)); - - React.useEffect(() => { - const handleStateChange = (value: S[T]) => { - setState(value); - }; - - const unsubscribeFunc = stateStore.subscribe(key, handleStateChange); - return unsubscribeFunc; - }, [key, stateStore]); - - const setter = React.useCallback( - function setter(valueOrFunc: S[T] | ((prev: S[T]) => S[T])): void { - if (valueOrFunc instanceof Function) { - const value = stateStore.getValue(key); - stateStore.setValue(key, valueOrFunc(value)); - return; - } - stateStore.setValue(key, valueOrFunc); - }, - [key, stateStore] - ); - - return [state, setter]; -} - -export function useStoreValue(stateStore: StateStore, key: T): S[T] { - const [state] = useStoreState(stateStore, key); - return state; -} - -export function useSetStoreValue( - stateStore: StateStore, - key: T -): (value: S[T] | ((prev: S[T]) => S[T])) => void { - const [, setter] = useStoreState(stateStore, key); - return setter; -} diff --git a/frontend/src/framework/StatusWriter.ts b/frontend/src/framework/StatusWriter.ts index ef95c2fa6..201fa8715 100644 --- a/frontend/src/framework/StatusWriter.ts +++ b/frontend/src/framework/StatusWriter.ts @@ -65,7 +65,7 @@ export class SettingsStatusWriter { } } -export function useViewStatusWriter(viewContext: ViewContext): ViewStatusWriter { +export function useViewStatusWriter(viewContext: ViewContext): ViewStatusWriter { const statusController = viewContext.getStatusController(); const statusWriter = React.useRef(new ViewStatusWriter(statusController)); @@ -80,7 +80,7 @@ export function useViewStatusWriter(viewContext: ViewContext return statusWriter.current; } -export function useSettingsStatusWriter(settingsContext: SettingsContext): SettingsStatusWriter { +export function useSettingsStatusWriter(settingsContext: SettingsContext): SettingsStatusWriter { const statusController = settingsContext.getStatusController(); const statusWriter = React.useRef(new SettingsStatusWriter(statusController)); diff --git a/frontend/src/framework/SyncSettings.ts b/frontend/src/framework/SyncSettings.ts index 5565494ca..bc2e192eb 100644 --- a/frontend/src/framework/SyncSettings.ts +++ b/frontend/src/framework/SyncSettings.ts @@ -45,13 +45,13 @@ export const SyncSettingsMeta = { export class SyncSettingsHelper { private _workbenchServices: WorkbenchServices; - private _moduleContext: SettingsContext | ViewContext | null; + private _moduleContext: SettingsContext | ViewContext | null; private _activeSyncedKeys: SyncSettingKey[]; constructor( activeSyncedKeys: SyncSettingKey[], workbenchServices: WorkbenchServices, - moduleContext?: SettingsContext | ViewContext + moduleContext?: SettingsContext | ViewContext ) { this._activeSyncedKeys = activeSyncedKeys; this._workbenchServices = workbenchServices; diff --git a/frontend/src/framework/UniDirectionalModuleComponentsInterface.ts b/frontend/src/framework/UniDirectionalModuleComponentsInterface.ts index ce1c3f410..b770f7b9a 100644 --- a/frontend/src/framework/UniDirectionalModuleComponentsInterface.ts +++ b/frontend/src/framework/UniDirectionalModuleComponentsInterface.ts @@ -19,16 +19,16 @@ export class UniDirectionalModuleComponentsInterface(key: T): Atom { - const derivedAtom = this._atoms.get(key); - if (derivedAtom) { - return derivedAtom as Atom; + const atom = this._atoms.get(key); + if (atom) { + return atom as Atom; } throw new Error(`Atom for key '${String(key)}' not found`); } } -export function useSettingsToViewInterfaceValue( +export function useInterfaceValue( interfaceInstance: UniDirectionalModuleComponentsInterface, key: K ): InterfaceType[K] { diff --git a/frontend/src/framework/Workbench.ts b/frontend/src/framework/Workbench.ts index 0bd6e13b7..71cbfe105 100644 --- a/frontend/src/framework/Workbench.ts +++ b/frontend/src/framework/Workbench.ts @@ -40,7 +40,7 @@ export type StoredUserEnsembleSetting = { }; export class Workbench { - private _moduleInstances: ModuleInstance[]; + private _moduleInstances: ModuleInstance[]; private _workbenchSession: WorkbenchSessionPrivate; private _workbenchServices: PrivateWorkbenchServices; private _workbenchSettings: PrivateWorkbenchSettings; @@ -113,11 +113,11 @@ export class Workbench { }; } - getModuleInstances(): ModuleInstance[] { + getModuleInstances(): ModuleInstance[] { return this._moduleInstances; } - getModuleInstance(id: string): ModuleInstance | undefined { + getModuleInstance(id: string): ModuleInstance | undefined { return this._moduleInstances.find((moduleInstance) => moduleInstance.getId() === id); } @@ -163,7 +163,7 @@ export class Workbench { this.notifySubscribers(WorkbenchEvents.ModuleInstancesChanged); } - makeAndAddModuleInstance(moduleName: string, layout: LayoutElement): ModuleInstance { + makeAndAddModuleInstance(moduleName: string, layout: LayoutElement): ModuleInstance { const module = ModuleRegistry.getModule(moduleName); if (!module) { throw new Error(`Module ${moduleName} not found`); diff --git a/frontend/src/framework/internal/ModuleNotFoundPlaceholder.tsx b/frontend/src/framework/internal/ModuleNotFoundPlaceholder.tsx index 349488bad..679fb9db8 100644 --- a/frontend/src/framework/internal/ModuleNotFoundPlaceholder.tsx +++ b/frontend/src/framework/internal/ModuleNotFoundPlaceholder.tsx @@ -4,12 +4,7 @@ import { Button } from "@lib/components/Button"; import { Tag } from "@lib/components/Tag"; import { BugReport, Forum, WebAssetOff } from "@mui/icons-material"; -export class ModuleNotFoundPlaceholder extends Module< - Record, - Record, - Record, - Record -> { +export class ModuleNotFoundPlaceholder extends Module { constructor(moduleName: string) { super({ name: moduleName, @@ -20,11 +15,8 @@ export class ModuleNotFoundPlaceholder extends Module< this._importState = ImportState.Imported; } - makeInstance( - instanceNumber: number - ): ModuleInstance, Record, Record, Record> { + makeInstance(instanceNumber: number): ModuleInstance { const instance = super.makeInstance(instanceNumber); - instance.setDefaultState({}); return instance; } diff --git a/frontend/src/framework/internal/components/ApplyInterfaceEffects/applyInterfaceEffects.tsx b/frontend/src/framework/internal/components/ApplyInterfaceEffects/applyInterfaceEffects.tsx new file mode 100644 index 000000000..e96822f62 --- /dev/null +++ b/frontend/src/framework/internal/components/ApplyInterfaceEffects/applyInterfaceEffects.tsx @@ -0,0 +1,25 @@ +import React from "react"; + +import { ModuleInterfaceTypes } from "@framework/Module"; +import { ModuleInstance } from "@framework/ModuleInstance"; + +import { useAtom } from "jotai"; + +export type ApplyInterfaceEffectsProps = { + moduleInstance: ModuleInstance; + children?: React.ReactNode; +}; + +export function ApplyInterfaceEffectsToView( + props: ApplyInterfaceEffectsProps +) { + useAtom(props.moduleInstance.getSettingsToViewInterfaceEffectsAtom()); + return <>{props.children}; +} + +export function ApplyInterfaceEffectsToSettings( + props: ApplyInterfaceEffectsProps +) { + useAtom(props.moduleInstance.getViewToSettingsInterfaceEffectsAtom()); + return <>{props.children}; +} diff --git a/frontend/src/framework/internal/components/ApplyInterfaceEffects/index.ts b/frontend/src/framework/internal/components/ApplyInterfaceEffects/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/private-components/channelReceiverNodesWrapper.tsx b/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/private-components/channelReceiverNodesWrapper.tsx index ad202abe6..7c450b3c5 100644 --- a/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/private-components/channelReceiverNodesWrapper.tsx +++ b/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/private-components/channelReceiverNodesWrapper.tsx @@ -14,7 +14,7 @@ import { ChannelReceiverNode } from "./channelReceiverNode"; export type ChannelReceiverNodesWrapperProps = { forwardedRef: React.RefObject; - moduleInstance: ModuleInstance; + moduleInstance: ModuleInstance; workbench: Workbench; }; diff --git a/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/private-components/header.tsx b/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/private-components/header.tsx index 90c74a77b..3266bbb50 100644 --- a/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/private-components/header.tsx +++ b/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/private-components/header.tsx @@ -22,7 +22,7 @@ import { resolveClassNames } from "@lib/utils/resolveClassNames"; import { Close, Error, History, Input, Output, Warning } from "@mui/icons-material"; export type HeaderProps = { - moduleInstance: ModuleInstance; + moduleInstance: ModuleInstance; isDragged: boolean; onPointerDown: (event: React.PointerEvent) => void; onRemoveClick: (event: React.PointerEvent) => void; diff --git a/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/private-components/viewContent.tsx b/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/private-components/viewContent.tsx index c0b959645..d1f0b2f6c 100644 --- a/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/private-components/viewContent.tsx +++ b/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/private-components/viewContent.tsx @@ -9,6 +9,7 @@ import { } from "@framework/ModuleInstance"; import { StatusSource } from "@framework/ModuleInstanceStatusController"; import { Workbench } from "@framework/Workbench"; +import { ApplyInterfaceEffectsToView } from "@framework/internal/components/ApplyInterfaceEffects/applyInterfaceEffects"; import { DebugProfiler } from "@framework/internal/components/DebugProfiler"; import { ErrorBoundary } from "@framework/internal/components/ErrorBoundary"; import { HydrateQueryClientAtom } from "@framework/internal/components/HydrateQueryClientAtom"; @@ -19,7 +20,7 @@ import { Provider } from "jotai"; import { CrashView } from "./crashView"; type ViewContentProps = { - moduleInstance: ModuleInstance; + moduleInstance: ModuleInstance; workbench: Workbench; }; @@ -27,7 +28,7 @@ export const ViewContent = React.memo((props: ViewContentProps) => { const importState = useModuleInstanceTopicValue(props.moduleInstance, ModuleInstanceTopic.IMPORT_STATE); const moduleInstanceState = useModuleInstanceTopicValue(props.moduleInstance, ModuleInstanceTopic.STATE); - const atomStore = props.moduleInstance.getAtomStore(); + const atomStore = props.workbench.getAtomStoreMaster().getAtomStoreForModuleInstance(props.moduleInstance.getId()); const handleModuleInstanceReload = React.useCallback( function handleModuleInstanceReload() { @@ -105,13 +106,15 @@ export const ViewContent = React.memo((props: ViewContentProps) => { > - + + + diff --git a/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/viewWrapper.tsx b/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/viewWrapper.tsx index b7b4440f5..597e045be 100644 --- a/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/viewWrapper.tsx +++ b/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/viewWrapper.tsx @@ -15,7 +15,7 @@ import { ViewWrapperPlaceholder } from "../viewWrapperPlaceholder"; type ViewWrapperProps = { isActive: boolean; - moduleInstance: ModuleInstance; + moduleInstance: ModuleInstance; workbench: Workbench; width: number; height: number; diff --git a/frontend/src/framework/internal/components/ErrorBoundary/errorBoundary.tsx b/frontend/src/framework/internal/components/ErrorBoundary/errorBoundary.tsx index 813bff763..f9497bf5b 100644 --- a/frontend/src/framework/internal/components/ErrorBoundary/errorBoundary.tsx +++ b/frontend/src/framework/internal/components/ErrorBoundary/errorBoundary.tsx @@ -3,7 +3,7 @@ import React from "react"; import { ModuleInstance } from "@framework/ModuleInstance"; export type Props = { - moduleInstance: ModuleInstance; + moduleInstance: ModuleInstance; children?: React.ReactNode; }; diff --git a/frontend/src/framework/internal/components/LeftSettingsPanel/private-components/moduleSettings.tsx b/frontend/src/framework/internal/components/LeftSettingsPanel/private-components/moduleSettings.tsx index c393ebaf6..65bdef672 100644 --- a/frontend/src/framework/internal/components/LeftSettingsPanel/private-components/moduleSettings.tsx +++ b/frontend/src/framework/internal/components/LeftSettingsPanel/private-components/moduleSettings.tsx @@ -16,11 +16,12 @@ import { Settings as SettingsIcon } from "@mui/icons-material"; import { Provider } from "jotai"; +import { ApplyInterfaceEffectsToSettings } from "../../ApplyInterfaceEffects/applyInterfaceEffects"; import { DebugProfiler } from "../../DebugProfiler"; import { HydrateQueryClientAtom } from "../../HydrateQueryClientAtom"; type ModuleSettingsProps = { - moduleInstance: ModuleInstance; + moduleInstance: ModuleInstance; activeModuleInstanceId: string; workbench: Workbench; }; @@ -28,7 +29,7 @@ type ModuleSettingsProps = { export const ModuleSettings: React.FC = (props) => { const importState = useModuleInstanceTopicValue(props.moduleInstance, ModuleInstanceTopic.IMPORT_STATE); const moduleInstanceState = useModuleInstanceTopicValue(props.moduleInstance, ModuleInstanceTopic.STATE); - const atomStore = props.moduleInstance.getAtomStore(); + const atomStore = props.workbench.getAtomStoreMaster().getAtomStoreForModuleInstance(props.moduleInstance.getId()); if (importState !== ImportState.Imported || !props.moduleInstance.isInitialized()) { return null; @@ -93,13 +94,15 @@ export const ModuleSettings: React.FC = (props) => { > - + + + diff --git a/frontend/src/framework/internal/components/LeftSettingsPanel/private-components/modulesList.tsx b/frontend/src/framework/internal/components/LeftSettingsPanel/private-components/modulesList.tsx index 82d5ca257..7c2a13a49 100644 --- a/frontend/src/framework/internal/components/LeftSettingsPanel/private-components/modulesList.tsx +++ b/frontend/src/framework/internal/components/LeftSettingsPanel/private-components/modulesList.tsx @@ -350,7 +350,7 @@ function makeDevStateIcon(devState: ModuleDevState): React.ReactNode { } type DetailsPopupProps = { - module: Module; + module: Module; left: number; top: number; onClose: () => void; diff --git a/frontend/src/framework/internal/components/RightSettingsPanel/private-components/ModuleInstanceLog/moduleInstanceLog.tsx b/frontend/src/framework/internal/components/RightSettingsPanel/private-components/ModuleInstanceLog/moduleInstanceLog.tsx index 4c7944240..cc4ce2077 100644 --- a/frontend/src/framework/internal/components/RightSettingsPanel/private-components/ModuleInstanceLog/moduleInstanceLog.tsx +++ b/frontend/src/framework/internal/components/RightSettingsPanel/private-components/ModuleInstanceLog/moduleInstanceLog.tsx @@ -162,7 +162,7 @@ export function ModuleInstanceLog(props: ModuleInstanceLogProps): React.ReactNod type LogListProps = { onShowDetails: (details: Record, yPos: number) => void; onHideDetails: () => void; - moduleInstance: ModuleInstance; + moduleInstance: ModuleInstance; }; function LogList(props: LogListProps): React.ReactNode { diff --git a/frontend/src/framework/internal/hooks/workbenchHooks.ts b/frontend/src/framework/internal/hooks/workbenchHooks.ts index 978b1c11f..3fbba8d1c 100644 --- a/frontend/src/framework/internal/hooks/workbenchHooks.ts +++ b/frontend/src/framework/internal/hooks/workbenchHooks.ts @@ -3,8 +3,8 @@ import React from "react"; import { ModuleInstance } from "@framework/ModuleInstance"; import { Workbench, WorkbenchEvents } from "@framework/Workbench"; -export function useModuleInstances(workbench: Workbench): ModuleInstance[] { - const [moduleInstances, setModuleInstances] = React.useState[]>([]); +export function useModuleInstances(workbench: Workbench): ModuleInstance[] { + const [moduleInstances, setModuleInstances] = React.useState[]>([]); React.useEffect(() => { function handleModuleInstancesChange() { diff --git a/frontend/src/modules/3DViewer/settingsToViewInterface.ts b/frontend/src/modules/3DViewer/interfaces.ts similarity index 52% rename from frontend/src/modules/3DViewer/settingsToViewInterface.ts rename to frontend/src/modules/3DViewer/interfaces.ts index bde016138..b74c6fb9f 100644 --- a/frontend/src/modules/3DViewer/settingsToViewInterface.ts +++ b/frontend/src/modules/3DViewer/interfaces.ts @@ -1,27 +1,45 @@ import { BoundingBox3d_api } from "@api"; +import { EnsembleIdent } from "@framework/EnsembleIdent"; import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; +import { IntersectionType } from "@framework/types/intersection"; import { ColorScale } from "@lib/utils/ColorScale"; import { + addCustomIntersectionPolylineEditModeActiveAtom, colorScaleAtom, + editCustomIntersectionPolylineEditModeActiveAtom, gridLayerAtom, intersectionExtensionLengthAtom, + intersectionTypeAtom, showGridlinesAtom, showIntersectionAtom, useCustomBoundsAtom, } from "./settings/atoms/baseAtoms"; import { + selectedCustomIntersectionPolylineIdAtom, + selectedEnsembleIdentAtom, selectedGridCellIndexRangesAtom, selectedGridModelBoundingBox3dAtom, selectedGridModelNameAtom, selectedGridModelParameterDateOrIntervalAtom, selectedGridModelParameterNameAtom, + selectedHighlightedWellboreUuidAtom, selectedRealizationAtom, selectedWellboreUuidsAtom, } from "./settings/atoms/derivedAtoms"; import { GridCellIndexRanges } from "./typesAndEnums"; +import { + editCustomIntersectionPolylineEditModeActiveAtom as viewEditCustomIntersectionPolylineEditModeActiveAtom, + intersectionTypeAtom as viewIntersectionTypeAtom, +} from "./view/atoms/baseAtoms"; export type SettingsToViewInterface = { + ensembleIdent: EnsembleIdent | null; + highlightedWellboreUuid: string | null; + customIntersectionPolylineId: string | null; + intersectionType: IntersectionType; + addCustomIntersectionPolylineEditModeActive: boolean; + editCustomIntersectionPolylineEditModeActive: boolean; showGridlines: boolean; showIntersection: boolean; gridLayer: number; @@ -37,7 +55,35 @@ export type SettingsToViewInterface = { gridCellIndexRanges: GridCellIndexRanges; }; -export const interfaceInitialization: InterfaceInitialization = { +export type ViewToSettingsInterface = { + editCustomIntersectionPolylineEditModeActive: boolean; + intersectionType: IntersectionType; +}; + +export type Interfaces = { + settingsToView: SettingsToViewInterface; + viewToSettings: ViewToSettingsInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { + ensembleIdent: (get) => { + return get(selectedEnsembleIdentAtom); + }, + highlightedWellboreUuid: (get) => { + return get(selectedHighlightedWellboreUuidAtom); + }, + customIntersectionPolylineId: (get) => { + return get(selectedCustomIntersectionPolylineIdAtom); + }, + intersectionType: (get) => { + return get(intersectionTypeAtom); + }, + addCustomIntersectionPolylineEditModeActive: (get) => { + return get(addCustomIntersectionPolylineEditModeActiveAtom); + }, + editCustomIntersectionPolylineEditModeActive: (get) => { + return get(editCustomIntersectionPolylineEditModeActiveAtom); + }, showGridlines: (get) => { return get(showGridlinesAtom); }, @@ -78,3 +124,12 @@ export const interfaceInitialization: InterfaceInitialization = { + editCustomIntersectionPolylineEditModeActive: (get) => { + return get(viewEditCustomIntersectionPolylineEditModeActiveAtom); + }, + intersectionType: (get) => { + return get(viewIntersectionTypeAtom); + }, +}; diff --git a/frontend/src/modules/3DViewer/loadModule.tsx b/frontend/src/modules/3DViewer/loadModule.tsx index 5853960e2..36deb122a 100644 --- a/frontend/src/modules/3DViewer/loadModule.tsx +++ b/frontend/src/modules/3DViewer/loadModule.tsx @@ -1,12 +1,18 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; +import { Interfaces, settingsToViewInterfaceInitialization, viewToSettingsInterfaceInitialization } from "./interfaces"; import { MODULE_NAME } from "./registerModule"; +import { viewToSettingsInterfaceEffects } from "./settings/atoms/interfaceEffects"; import { Settings } from "./settings/settings"; -import { SettingsToViewInterface, interfaceInitialization } from "./settingsToViewInterface"; -import { State } from "./state"; +import { settingsToViewInterfaceEffects } from "./view/atoms/interfaceEffects"; import { View } from "./view/view"; -const module = ModuleRegistry.initModule(MODULE_NAME, {}, {}, interfaceInitialization); +const module = ModuleRegistry.initModule(MODULE_NAME, { + settingsToViewInterfaceInitialization, + viewToSettingsInterfaceInitialization, + viewToSettingsInterfaceEffects, + settingsToViewInterfaceEffects, +}); module.viewFC = View; module.settingsFC = Settings; diff --git a/frontend/src/modules/3DViewer/registerModule.ts b/frontend/src/modules/3DViewer/registerModule.ts index 2c9f9a3f3..86b8ec946 100644 --- a/frontend/src/modules/3DViewer/registerModule.ts +++ b/frontend/src/modules/3DViewer/registerModule.ts @@ -3,15 +3,14 @@ import { ModuleDataTagId } from "@framework/ModuleDataTags"; import { ModuleRegistry } from "@framework/ModuleRegistry"; import { SyncSettingKey } from "@framework/SyncSettings"; +import { Interfaces } from "./interfaces"; import { preview } from "./preview"; -import { SettingsToViewInterface } from "./settingsToViewInterface"; -import { State } from "./state"; export const MODULE_NAME = "3DViewer"; const description = "Generic 3D viewer for grid, surfaces, and wells."; -ModuleRegistry.registerModule({ +ModuleRegistry.registerModule({ moduleName: MODULE_NAME, defaultTitle: "3D Viewer", category: ModuleCategory.MAIN, diff --git a/frontend/src/modules/3DViewer/settings/atoms/baseAtoms.ts b/frontend/src/modules/3DViewer/settings/atoms/baseAtoms.ts index 21a6d836e..72c62537b 100644 --- a/frontend/src/modules/3DViewer/settings/atoms/baseAtoms.ts +++ b/frontend/src/modules/3DViewer/settings/atoms/baseAtoms.ts @@ -1,4 +1,5 @@ import { EnsembleIdent } from "@framework/EnsembleIdent"; +import { IntersectionType } from "@framework/types/intersection"; import { ColorScale } from "@lib/utils/ColorScale"; import { GridCellIndexRanges } from "@modules/3DViewer/typesAndEnums"; @@ -10,6 +11,10 @@ export const gridLayerAtom = atom(1); export const intersectionExtensionLengthAtom = atom(1000); export const colorScaleAtom = atom(null); export const useCustomBoundsAtom = atom(false); +export const intersectionTypeAtom = atom(IntersectionType.WELLBORE); +export const addCustomIntersectionPolylineEditModeActiveAtom = atom(false); +export const editCustomIntersectionPolylineEditModeActiveAtom = atom(false); +export const currentCustomIntersectionPolylineAtom = atom([]); export const userSelectedEnsembleIdentAtom = atom(null); export const userSelectedRealizationAtom = atom(null); diff --git a/frontend/src/modules/3DViewer/settings/atoms/derivedAtoms.ts b/frontend/src/modules/3DViewer/settings/atoms/derivedAtoms.ts index af39bf4f3..a646a9785 100644 --- a/frontend/src/modules/3DViewer/settings/atoms/derivedAtoms.ts +++ b/frontend/src/modules/3DViewer/settings/atoms/derivedAtoms.ts @@ -2,21 +2,70 @@ import { Grid3dDimensions_api } from "@api"; import { EnsembleIdent } from "@framework/EnsembleIdent"; import { EnsembleRealizationFilterFunctionAtom, EnsembleSetAtom } from "@framework/GlobalAtoms"; import { IntersectionPolylinesAtom } from "@framework/userCreatedItems/IntersectionPolylines"; -import { selectedEnsembleIdentAtom } from "@modules/3DViewer/sharedAtoms/sharedAtoms"; import { GridCellIndexRanges } from "@modules/3DViewer/typesAndEnums"; import { atom } from "jotai"; import { + userSelectedCustomIntersectionPolylineIdAtom, + userSelectedEnsembleIdentAtom, userSelectedGridCellIndexRangesAtom, userSelectedGridModelNameAtom, userSelectedGridModelParameterDateOrIntervalAtom, userSelectedGridModelParameterNameAtom, + userSelectedHighlightedWellboreUuidAtom, userSelectedRealizationAtom, userSelectedWellboreUuidsAtom, } from "./baseAtoms"; import { drilledWellboreHeadersQueryAtom, gridModelInfosQueryAtom } from "./queryAtoms"; +export const selectedEnsembleIdentAtom = atom((get) => { + const ensembleSet = get(EnsembleSetAtom); + const userSelectedEnsembleIdent = get(userSelectedEnsembleIdentAtom); + + if (userSelectedEnsembleIdent === null || !ensembleSet.hasEnsemble(userSelectedEnsembleIdent)) { + return ensembleSet.getEnsembleArr()[0]?.getIdent() || null; + } + + return userSelectedEnsembleIdent; +}); + +export const selectedHighlightedWellboreUuidAtom = atom((get) => { + const userSelectedHighlightedWellboreUuid = get(userSelectedHighlightedWellboreUuidAtom); + const wellboreHeaders = get(drilledWellboreHeadersQueryAtom); + + if (!wellboreHeaders.data) { + return null; + } + + if ( + !userSelectedHighlightedWellboreUuid || + !wellboreHeaders.data.some((el) => el.wellboreUuid === userSelectedHighlightedWellboreUuid) + ) { + return wellboreHeaders.data[0]?.wellboreUuid ?? null; + } + + return userSelectedHighlightedWellboreUuid; +}); + +export const selectedCustomIntersectionPolylineIdAtom = atom((get) => { + const userSelectedCustomIntersectionPolylineId = get(userSelectedCustomIntersectionPolylineIdAtom); + const customIntersectionPolylines = get(IntersectionPolylinesAtom); + + if (!customIntersectionPolylines.length) { + return null; + } + + if ( + !userSelectedCustomIntersectionPolylineId || + !customIntersectionPolylines.some((el) => el.id === userSelectedCustomIntersectionPolylineId) + ) { + return customIntersectionPolylines[0].id; + } + + return userSelectedCustomIntersectionPolylineId; +}); + export const availableRealizationsAtom = atom((get) => { const ensembleSet = get(EnsembleSetAtom); const selectedEnsembleIdent = get(selectedEnsembleIdentAtom); diff --git a/frontend/src/modules/3DViewer/settings/atoms/interfaceEffects.ts b/frontend/src/modules/3DViewer/settings/atoms/interfaceEffects.ts new file mode 100644 index 000000000..c0a351aff --- /dev/null +++ b/frontend/src/modules/3DViewer/settings/atoms/interfaceEffects.ts @@ -0,0 +1,17 @@ +import { InterfaceEffects } from "@framework/Module"; +import { ViewToSettingsInterface } from "@modules/3DViewer/interfaces"; + +import { editCustomIntersectionPolylineEditModeActiveAtom, intersectionTypeAtom } from "./baseAtoms"; + +export const viewToSettingsInterfaceEffects: InterfaceEffects = [ + (getInterfaceValue, setAtomValue) => { + const editCustomIntersectionPolylineEditModeActive = getInterfaceValue( + "editCustomIntersectionPolylineEditModeActive" + ); + setAtomValue(editCustomIntersectionPolylineEditModeActiveAtom, editCustomIntersectionPolylineEditModeActive); + }, + (getInterfaceValue, setAtomValue) => { + const viewIntersectionType = getInterfaceValue("intersectionType"); + setAtomValue(intersectionTypeAtom, viewIntersectionType); + }, +]; diff --git a/frontend/src/modules/3DViewer/settings/atoms/queryAtoms.ts b/frontend/src/modules/3DViewer/settings/atoms/queryAtoms.ts index 8b7c2b8df..9c95115a2 100644 --- a/frontend/src/modules/3DViewer/settings/atoms/queryAtoms.ts +++ b/frontend/src/modules/3DViewer/settings/atoms/queryAtoms.ts @@ -1,10 +1,9 @@ import { apiService } from "@framework/ApiService"; import { EnsembleSetAtom } from "@framework/GlobalAtoms"; -import { selectedEnsembleIdentAtom } from "@modules/3DViewer/sharedAtoms/sharedAtoms"; import { atomWithQuery } from "jotai-tanstack-query"; -import { selectedRealizationAtom } from "./derivedAtoms"; +import { selectedEnsembleIdentAtom, selectedRealizationAtom } from "./derivedAtoms"; const STALE_TIME = 60 * 1000; const CACHE_TIME = 60 * 1000; diff --git a/frontend/src/modules/3DViewer/settings/settings.tsx b/frontend/src/modules/3DViewer/settings/settings.tsx index d6173c455..f02cdb7d1 100644 --- a/frontend/src/modules/3DViewer/settings/settings.tsx +++ b/frontend/src/modules/3DViewer/settings/settings.tsx @@ -30,8 +30,11 @@ import { useAtom, useAtomValue, useSetAtom } from "jotai"; import { isEqual } from "lodash"; import { + addCustomIntersectionPolylineEditModeActiveAtom, colorScaleAtom, + editCustomIntersectionPolylineEditModeActiveAtom, intersectionExtensionLengthAtom, + intersectionTypeAtom, showGridlinesAtom, showIntersectionAtom, useCustomBoundsAtom, @@ -48,10 +51,12 @@ import { import { availableRealizationsAtom, gridModelDimensionsAtom, + selectedEnsembleIdentAtom, selectedGridCellIndexRangesAtom, selectedGridModelNameAtom, selectedGridModelParameterDateOrIntervalAtom, selectedGridModelParameterNameAtom, + selectedHighlightedWellboreUuidAtom, selectedRealizationAtom, selectedWellboreUuidsAtom, } from "./atoms/derivedAtoms"; @@ -59,18 +64,10 @@ import { drilledWellboreHeadersQueryAtom, gridModelInfosQueryAtom } from "./atom import { GridCellIndexFilter } from "./components/gridCellIndexFilter"; import { WellboreSelector } from "./components/wellboreSelector"; -import { SettingsToViewInterface } from "../settingsToViewInterface"; -import { - addCustomIntersectionPolylineEditModeActiveAtom, - editCustomIntersectionPolylineEditModeActiveAtom, - intersectionTypeAtom, - selectedEnsembleIdentAtom, - selectedHighlightedWellboreUuidAtom, -} from "../sharedAtoms/sharedAtoms"; -import { State } from "../state"; +import { Interfaces } from "../interfaces"; import { GridCellIndexRanges } from "../typesAndEnums"; -export function Settings(props: ModuleSettingsProps): JSX.Element { +export function Settings(props: ModuleSettingsProps): JSX.Element { const ensembleSet = useEnsembleSet(props.workbenchSession); const statusWriter = useSettingsStatusWriter(props.settingsContext); diff --git a/frontend/src/modules/3DViewer/sharedAtoms/sharedAtoms.ts b/frontend/src/modules/3DViewer/sharedAtoms/sharedAtoms.ts deleted file mode 100644 index 97b851aef..000000000 --- a/frontend/src/modules/3DViewer/sharedAtoms/sharedAtoms.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* -Note that shared atoms is just a temporary solution to a use case that does not have a clear solution yet. -This is not how it should be done properly, communication between settings and view components should be done -through the use of interfaces. -*/ -import { EnsembleIdent } from "@framework/EnsembleIdent"; -import { EnsembleSetAtom } from "@framework/GlobalAtoms"; -import { IntersectionType } from "@framework/types/intersection"; -import { IntersectionPolylinesAtom } from "@framework/userCreatedItems/IntersectionPolylines"; - -import { atom } from "jotai"; - -import { - userSelectedCustomIntersectionPolylineIdAtom, - userSelectedEnsembleIdentAtom, - userSelectedHighlightedWellboreUuidAtom, -} from "../settings/atoms/baseAtoms"; -import { drilledWellboreHeadersQueryAtom } from "../settings/atoms/queryAtoms"; - -export const selectedEnsembleIdentAtom = atom((get) => { - const ensembleSet = get(EnsembleSetAtom); - const userSelectedEnsembleIdent = get(userSelectedEnsembleIdentAtom); - - if (userSelectedEnsembleIdent === null || !ensembleSet.hasEnsemble(userSelectedEnsembleIdent)) { - return ensembleSet.getEnsembleArr()[0]?.getIdent() || null; - } - - return userSelectedEnsembleIdent; -}); - -export const selectedHighlightedWellboreUuidAtom = atom((get) => { - const userSelectedHighlightedWellboreUuid = get(userSelectedHighlightedWellboreUuidAtom); - const wellboreHeaders = get(drilledWellboreHeadersQueryAtom); - - if (!wellboreHeaders.data) { - return null; - } - - if ( - !userSelectedHighlightedWellboreUuid || - !wellboreHeaders.data.some((el) => el.wellboreUuid === userSelectedHighlightedWellboreUuid) - ) { - return wellboreHeaders.data[0]?.wellboreUuid ?? null; - } - - return userSelectedHighlightedWellboreUuid; -}); - -export const intersectionTypeAtom = atom(IntersectionType.WELLBORE); -export const addCustomIntersectionPolylineEditModeActiveAtom = atom(false); -export const editCustomIntersectionPolylineEditModeActiveAtom = atom(false); - -export const currentCustomIntersectionPolylineAtom = atom([]); - -export const selectedCustomIntersectionPolylineIdAtom = atom((get) => { - const userSelectedCustomIntersectionPolylineId = get(userSelectedCustomIntersectionPolylineIdAtom); - const customIntersectionPolylines = get(IntersectionPolylinesAtom); - - if (!customIntersectionPolylines.length) { - return null; - } - - if ( - !userSelectedCustomIntersectionPolylineId || - !customIntersectionPolylines.some((el) => el.id === userSelectedCustomIntersectionPolylineId) - ) { - return customIntersectionPolylines[0].id; - } - - return userSelectedCustomIntersectionPolylineId; -}); diff --git a/frontend/src/modules/3DViewer/state.ts b/frontend/src/modules/3DViewer/state.ts deleted file mode 100644 index 90ff7f709..000000000 --- a/frontend/src/modules/3DViewer/state.ts +++ /dev/null @@ -1 +0,0 @@ -export type State = Record; diff --git a/frontend/src/modules/3DViewer/typesAndEnums.ts b/frontend/src/modules/3DViewer/typesAndEnums.ts index 209be6c6b..de3bd1dc5 100644 --- a/frontend/src/modules/3DViewer/typesAndEnums.ts +++ b/frontend/src/modules/3DViewer/typesAndEnums.ts @@ -1,14 +1,3 @@ -export enum IntersectionType { - CUSTOM_POLYLINE = "custom-polyline", - WELLBORE = "wellbore", -} - -export type CustomIntersectionPolyline = { - id: string; - name: string; - polyline: number[][]; -}; - export type GridCellIndexRanges = { i: [number, number]; j: [number, number]; diff --git a/frontend/src/modules/3DViewer/view/atoms/baseAtoms.ts b/frontend/src/modules/3DViewer/view/atoms/baseAtoms.ts new file mode 100644 index 000000000..938939ea9 --- /dev/null +++ b/frontend/src/modules/3DViewer/view/atoms/baseAtoms.ts @@ -0,0 +1,11 @@ +import { EnsembleIdent } from "@framework/EnsembleIdent"; +import { IntersectionType } from "@framework/types/intersection"; + +import { atom } from "jotai"; + +export const intersectionTypeAtom = atom(IntersectionType.WELLBORE); +export const editCustomIntersectionPolylineEditModeActiveAtom = atom(false); + +export const ensembleIdentAtom = atom(null); +export const highlightedWellboreUuidAtom = atom(null); +export const customIntersectionPolylineIdAtom = atom(null); diff --git a/frontend/src/modules/3DViewer/view/atoms/derivedAtoms.ts b/frontend/src/modules/3DViewer/view/atoms/derivedAtoms.ts index ba6b98cb7..5bf0c16c5 100644 --- a/frontend/src/modules/3DViewer/view/atoms/derivedAtoms.ts +++ b/frontend/src/modules/3DViewer/view/atoms/derivedAtoms.ts @@ -1,28 +1,17 @@ import { IntersectionReferenceSystem } from "@equinor/esv-intersection"; import { IntersectionType } from "@framework/types/intersection"; import { IntersectionPolylinesAtom } from "@framework/userCreatedItems/IntersectionPolylines"; -import { - intersectionTypeAtom, - selectedCustomIntersectionPolylineIdAtom, - selectedHighlightedWellboreUuidAtom, -} from "@modules/3DViewer/sharedAtoms/sharedAtoms"; import { atom } from "jotai"; +import { customIntersectionPolylineIdAtom, highlightedWellboreUuidAtom, intersectionTypeAtom } from "./baseAtoms"; import { fieldWellboreTrajectoriesQueryAtom } from "./queryAtoms"; -export const selectedCustomIntersectionPolylineAtom = atom((get) => { - const customIntersectionPolylineId = get(selectedCustomIntersectionPolylineIdAtom); - const customIntersectionPolylines = get(IntersectionPolylinesAtom); - - return customIntersectionPolylines.find((el) => el.id === customIntersectionPolylineId); -}); - export const intersectionReferenceSystemAtom = atom((get) => { const fieldWellboreTrajectories = get(fieldWellboreTrajectoriesQueryAtom); - const wellboreUuid = get(selectedHighlightedWellboreUuidAtom); + const wellboreUuid = get(highlightedWellboreUuidAtom); const customIntersectionPolylines = get(IntersectionPolylinesAtom); - const customIntersectionPolylineId = get(selectedCustomIntersectionPolylineIdAtom); + const customIntersectionPolylineId = get(customIntersectionPolylineIdAtom); const customIntersectionPolyline = customIntersectionPolylines.find((el) => el.id === customIntersectionPolylineId); @@ -66,3 +55,10 @@ export const intersectionReferenceSystemAtom = atom((get) => { return null; }); + +export const selectedCustomIntersectionPolylineAtom = atom((get) => { + const customIntersectionPolylineId = get(customIntersectionPolylineIdAtom); + const customIntersectionPolylines = get(IntersectionPolylinesAtom); + + return customIntersectionPolylines.find((el) => el.id === customIntersectionPolylineId) ?? null; +}); diff --git a/frontend/src/modules/3DViewer/view/atoms/interfaceEffects.ts b/frontend/src/modules/3DViewer/view/atoms/interfaceEffects.ts new file mode 100644 index 000000000..d18ff31f7 --- /dev/null +++ b/frontend/src/modules/3DViewer/view/atoms/interfaceEffects.ts @@ -0,0 +1,28 @@ +import { InterfaceEffects } from "@framework/Module"; +import { SettingsToViewInterface } from "@modules/3DViewer/interfaces"; + +import { + customIntersectionPolylineIdAtom, + ensembleIdentAtom, + highlightedWellboreUuidAtom, + intersectionTypeAtom, +} from "./baseAtoms"; + +export const settingsToViewInterfaceEffects: InterfaceEffects = [ + (getInterfaceValue, setAtomValue) => { + const ensembleIdent = getInterfaceValue("ensembleIdent"); + setAtomValue(ensembleIdentAtom, ensembleIdent); + }, + (getInterfaceValue, setAtomValue) => { + const highlightedWellboreUuid = getInterfaceValue("highlightedWellboreUuid"); + setAtomValue(highlightedWellboreUuidAtom, highlightedWellboreUuid); + }, + (getInterfaceValue, setAtomValue) => { + const customIntersectionPolylineId = getInterfaceValue("customIntersectionPolylineId"); + setAtomValue(customIntersectionPolylineIdAtom, customIntersectionPolylineId); + }, + (getInterfaceValue, setAtomValue) => { + const intersectionType = getInterfaceValue("intersectionType"); + setAtomValue(intersectionTypeAtom, intersectionType); + }, +]; diff --git a/frontend/src/modules/3DViewer/view/atoms/queryAtoms.ts b/frontend/src/modules/3DViewer/view/atoms/queryAtoms.ts index 5a1e62160..f7e220120 100644 --- a/frontend/src/modules/3DViewer/view/atoms/queryAtoms.ts +++ b/frontend/src/modules/3DViewer/view/atoms/queryAtoms.ts @@ -1,14 +1,15 @@ import { apiService } from "@framework/ApiService"; import { EnsembleSetAtom } from "@framework/GlobalAtoms"; -import { selectedEnsembleIdentAtom } from "@modules/3DViewer/sharedAtoms/sharedAtoms"; import { atomWithQuery } from "jotai-tanstack-query"; +import { ensembleIdentAtom } from "./baseAtoms"; + const STALE_TIME = 60 * 1000; const CACHE_TIME = 60 * 1000; export const fieldWellboreTrajectoriesQueryAtom = atomWithQuery((get) => { - const ensembleIdent = get(selectedEnsembleIdentAtom); + const ensembleIdent = get(ensembleIdentAtom); const ensembleSet = get(EnsembleSetAtom); let fieldIdentifier: string | null = null; diff --git a/frontend/src/modules/3DViewer/view/components/HoverUpdateWrapper.tsx b/frontend/src/modules/3DViewer/view/components/HoverUpdateWrapper.tsx index f8a1ee8af..381799b0a 100644 --- a/frontend/src/modules/3DViewer/view/components/HoverUpdateWrapper.tsx +++ b/frontend/src/modules/3DViewer/view/components/HoverUpdateWrapper.tsx @@ -13,7 +13,7 @@ export type HoverUpdateWrapperProps = { wellboreUuid: string | null; intersectionReferenceSystem?: IntersectionReferenceSystem; workbenchServices: WorkbenchServices; - viewContext: ViewContext; + viewContext: ViewContext; } & SubsurfaceViewerWrapperProps; export function HoverUpdateWrapper(props: HoverUpdateWrapperProps): React.ReactNode { diff --git a/frontend/src/modules/3DViewer/view/components/SyncedSettingsUpdateWrapper.tsx b/frontend/src/modules/3DViewer/view/components/SyncedSettingsUpdateWrapper.tsx index dd88870c3..41d669c47 100644 --- a/frontend/src/modules/3DViewer/view/components/SyncedSettingsUpdateWrapper.tsx +++ b/frontend/src/modules/3DViewer/view/components/SyncedSettingsUpdateWrapper.tsx @@ -8,7 +8,7 @@ import { HoverUpdateWrapper, HoverUpdateWrapperProps } from "./HoverUpdateWrappe export type SyncedSettingsUpdateWrapperProps = { workbenchServices: WorkbenchServices; - viewContext: ViewContext; + viewContext: ViewContext; } & HoverUpdateWrapperProps; export function SyncedSettingsUpdateWrapper(props: SyncedSettingsUpdateWrapperProps): React.ReactNode { diff --git a/frontend/src/modules/3DViewer/view/view.tsx b/frontend/src/modules/3DViewer/view/view.tsx index 6c106b539..248d16b71 100644 --- a/frontend/src/modules/3DViewer/view/view.tsx +++ b/frontend/src/modules/3DViewer/view/view.tsx @@ -16,25 +16,19 @@ import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; import { calcExtendedSimplifiedWellboreTrajectoryInXYPlane } from "@modules/_shared/utils/wellbore"; import { NorthArrow3DLayer } from "@webviz/subsurface-viewer/dist/layers"; -import { useAtom, useAtomValue } from "jotai"; +import { useAtom, useSetAtom } from "jotai"; +import { editCustomIntersectionPolylineEditModeActiveAtom, intersectionTypeAtom } from "./atoms/baseAtoms"; import { SyncedSettingsUpdateWrapper } from "./components/SyncedSettingsUpdateWrapper"; import { useGridParameterQuery, useGridSurfaceQuery } from "./queries/gridQueries"; import { useGridPolylineIntersection as useGridPolylineIntersectionQuery } from "./queries/polylineIntersection"; import { useWellboreCasingsQuery } from "./queries/wellboreSchematicsQueries"; import { makeAxesLayer, makeGrid3DLayer, makeIntersectionLayer, makeWellsLayer } from "./utils/layers"; +import { Interfaces } from "../interfaces"; import { userSelectedCustomIntersectionPolylineIdAtom } from "../settings/atoms/baseAtoms"; -import { SettingsToViewInterface } from "../settingsToViewInterface"; -import { - editCustomIntersectionPolylineEditModeActiveAtom, - intersectionTypeAtom, - selectedEnsembleIdentAtom, - selectedHighlightedWellboreUuidAtom, -} from "../sharedAtoms/sharedAtoms"; -import { State } from "../state"; - -export function View(props: ModuleViewProps): React.ReactNode { + +export function View(props: ModuleViewProps): React.ReactNode { const statusWriter = useViewStatusWriter(props.viewContext); const syncedSettingKeys = props.viewContext.useSyncedSettingKeys(); const syncHelper = new SyncSettingsHelper(syncedSettingKeys, props.workbenchServices); @@ -49,11 +43,10 @@ export function View(props: ModuleViewProps): Re const useCustomBounds = props.viewContext.useSettingsToViewInterfaceValue("useCustomBounds"); - const highlightedWellboreUuid = useAtomValue(selectedHighlightedWellboreUuidAtom); - - const ensembleIdent = useAtomValue(selectedEnsembleIdentAtom); const intersectionPolylines = useIntersectionPolylines(props.workbenchSession); + const ensembleIdent = props.viewContext.useSettingsToViewInterfaceValue("ensembleIdent"); + const highlightedWellboreUuid = props.viewContext.useSettingsToViewInterfaceValue("highlightedWellboreUuid"); const realization = props.viewContext.useSettingsToViewInterfaceValue("realization"); const wellboreUuids = props.viewContext.useSettingsToViewInterfaceValue("wellboreUuids"); const gridModelName = props.viewContext.useSettingsToViewInterfaceValue("gridModelName"); @@ -62,6 +55,15 @@ export function View(props: ModuleViewProps): Re const gridModelParameterDateOrInterval = props.viewContext.useSettingsToViewInterfaceValue( "gridModelParameterDateOrInterval" ); + + const editPolylineModeActive = props.viewContext.useSettingsToViewInterfaceValue( + "editCustomIntersectionPolylineEditModeActive" + ); + const setEditPolylineModeActive = useSetAtom(editCustomIntersectionPolylineEditModeActiveAtom); + + const intersectionType = props.viewContext.useSettingsToViewInterfaceValue("intersectionType"); + const setIntersectionType = useSetAtom(intersectionTypeAtom); + const ensembleSet = useEnsembleSet(props.workbenchSession); React.useEffect( @@ -85,10 +87,6 @@ export function View(props: ModuleViewProps): Re const intersectionExtensionLength = props.viewContext.useSettingsToViewInterfaceValue("intersectionExtensionLength"); - const [editPolylineModeActive, setEditPolylineModeActive] = useAtom( - editCustomIntersectionPolylineEditModeActiveAtom - ); - const [intersectionType, setIntersectionType] = useAtom(intersectionTypeAtom); const [selectedCustomIntersectionPolylineId, setSelectedCustomIntersectionPolylineId] = useAtom( userSelectedCustomIntersectionPolylineIdAtom diff --git a/frontend/src/modules/DbgWorkbenchSpy/implementation.tsx b/frontend/src/modules/DbgWorkbenchSpy/implementation.tsx index b7b0b5b7a..301600556 100644 --- a/frontend/src/modules/DbgWorkbenchSpy/implementation.tsx +++ b/frontend/src/modules/DbgWorkbenchSpy/implementation.tsx @@ -1,19 +1,21 @@ import React from "react"; import { EnsembleSet } from "@framework/EnsembleSet"; -import { ModuleSettingsProps, ModuleViewProps } from "@framework/Module"; +import { ModuleViewProps } from "@framework/Module"; import { AllTopicDefinitions, WorkbenchServices } from "@framework/WorkbenchServices"; import { useEnsembleSet } from "@framework/WorkbenchSession"; import { timestampUtcMsToIsoString } from "@framework/utils/timestampUtils"; import { Button } from "@lib/components/Button"; -export type SharedState = { - triggeredRefreshCounter: number; -}; +import { atom, useSetAtom } from "jotai"; + +import { Interfaces } from "./interfaces"; + +export const triggeredRefreshCounterAtom = atom(0); //----------------------------------------------------------------------------------------------------------- -export function WorkbenchSpySettings(props: ModuleSettingsProps) { - const setRefreshCounter = props.settingsContext.useSetStoreValue("triggeredRefreshCounter"); +export function WorkbenchSpySettings() { + const setRefreshCounter = useSetAtom(triggeredRefreshCounterAtom); return (
@@ -22,14 +24,14 @@ export function WorkbenchSpySettings(props: ModuleSettingsProps) { } //----------------------------------------------------------------------------------------------------------- -export function WorkbenchSpyView(props: ModuleViewProps) { +export function WorkbenchSpyView(props: ModuleViewProps) { const ensembleSet = useEnsembleSet(props.workbenchSession); const [hoverRealization, hoverRealization_TS] = useServiceValueWithTS( "global.hoverRealization", props.workbenchServices ); const [hoverTimestamp, hoverTimestamp_TS] = useServiceValueWithTS("global.hoverTimestamp", props.workbenchServices); - const triggeredRefreshCounter = props.viewContext.useStoreValue("triggeredRefreshCounter"); + const triggeredRefreshCounter = props.viewContext.useSettingsToViewInterfaceValue("triggeredRefreshCounter"); const componentRenderCount = React.useRef(0); React.useEffect(function incrementComponentRenderCount() { diff --git a/frontend/src/modules/DbgWorkbenchSpy/interfaces.ts b/frontend/src/modules/DbgWorkbenchSpy/interfaces.ts new file mode 100644 index 000000000..e7765eecb --- /dev/null +++ b/frontend/src/modules/DbgWorkbenchSpy/interfaces.ts @@ -0,0 +1,15 @@ +import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; + +import { triggeredRefreshCounterAtom } from "./implementation"; + +type SettingsToViewInterface = { + triggeredRefreshCounter: number; +}; + +export type Interfaces = { + settingsToView: SettingsToViewInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { + triggeredRefreshCounter: (get) => get(triggeredRefreshCounterAtom), +}; diff --git a/frontend/src/modules/DbgWorkbenchSpy/loadModule.tsx b/frontend/src/modules/DbgWorkbenchSpy/loadModule.tsx index a0e7767d7..60898b4d6 100644 --- a/frontend/src/modules/DbgWorkbenchSpy/loadModule.tsx +++ b/frontend/src/modules/DbgWorkbenchSpy/loadModule.tsx @@ -1,12 +1,11 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; -import { SharedState, WorkbenchSpySettings, WorkbenchSpyView } from "./implementation"; +import { WorkbenchSpySettings, WorkbenchSpyView } from "./implementation"; +import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; -const defaultState: SharedState = { - triggeredRefreshCounter: 0, -}; - -const module = ModuleRegistry.initModule("DbgWorkbenchSpy", defaultState); +const module = ModuleRegistry.initModule("DbgWorkbenchSpy", { + settingsToViewInterfaceInitialization, +}); module.viewFC = WorkbenchSpyView; module.settingsFC = WorkbenchSpySettings; diff --git a/frontend/src/modules/DbgWorkbenchSpy/registerModule.ts b/frontend/src/modules/DbgWorkbenchSpy/registerModule.ts index 274b3ddeb..2f9b87d95 100644 --- a/frontend/src/modules/DbgWorkbenchSpy/registerModule.ts +++ b/frontend/src/modules/DbgWorkbenchSpy/registerModule.ts @@ -1,9 +1,9 @@ import { ModuleCategory, ModuleDevState } from "@framework/Module"; import { ModuleRegistry } from "@framework/ModuleRegistry"; -import { SharedState } from "./implementation"; +import { Interfaces } from "./interfaces"; -ModuleRegistry.registerModule({ +ModuleRegistry.registerModule({ moduleName: "DbgWorkbenchSpy", defaultTitle: "Debug Workbench Spy", category: ModuleCategory.DEBUG, diff --git a/frontend/src/modules/DistributionPlot/interfaces.ts b/frontend/src/modules/DistributionPlot/interfaces.ts new file mode 100644 index 000000000..a554388ef --- /dev/null +++ b/frontend/src/modules/DistributionPlot/interfaces.ts @@ -0,0 +1,20 @@ +import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; + +import { numBinsAtom, orientationAtom, plotTypeAtom } from "./settings/atoms/baseAtoms"; +import { PlotType } from "./typesAndEnums"; + +type SettingsToViewInterface = { + plotType: PlotType | null; + numBins: number; + orientation: "h" | "v"; +}; + +export type Interfaces = { + settingsToView: SettingsToViewInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { + plotType: (get) => get(plotTypeAtom), + numBins: (get) => get(numBinsAtom), + orientation: (get) => get(orientationAtom), +}; diff --git a/frontend/src/modules/DistributionPlot/loadModule.tsx b/frontend/src/modules/DistributionPlot/loadModule.tsx index 11882b050..6ec0fe391 100644 --- a/frontend/src/modules/DistributionPlot/loadModule.tsx +++ b/frontend/src/modules/DistributionPlot/loadModule.tsx @@ -1,16 +1,12 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; -import { Settings } from "./settings"; -import { PlotType, State } from "./state"; +import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; +import { Settings } from "./settings/settings"; import { View } from "./view"; -const defaultState: State = { - plotType: PlotType.Histogram, - numBins: 10, - orientation: "h", -}; - -const module = ModuleRegistry.initModule("DistributionPlot", defaultState); +const module = ModuleRegistry.initModule("DistributionPlot", { + settingsToViewInterfaceInitialization, +}); module.viewFC = View; module.settingsFC = Settings; diff --git a/frontend/src/modules/DistributionPlot/registerModule.ts b/frontend/src/modules/DistributionPlot/registerModule.ts index 5fea31fa8..6563734ce 100644 --- a/frontend/src/modules/DistributionPlot/registerModule.ts +++ b/frontend/src/modules/DistributionPlot/registerModule.ts @@ -2,14 +2,14 @@ import { ModuleCategory, ModuleDevState } from "@framework/Module"; import { ModuleRegistry } from "@framework/ModuleRegistry"; import { SyncSettingKey } from "@framework/SyncSettings"; +import { Interfaces } from "./interfaces"; import { preview } from "./preview"; import { receiverDefs } from "./receiverDefs"; -import { State } from "./state"; const description = "Sub-module that can be connected to other modules via data channels for visualization of distribution data."; -ModuleRegistry.registerModule({ +ModuleRegistry.registerModule({ moduleName: "DistributionPlot", defaultTitle: "Distribution plot", category: ModuleCategory.SUB, diff --git a/frontend/src/modules/DistributionPlot/settings/atoms/baseAtoms.ts b/frontend/src/modules/DistributionPlot/settings/atoms/baseAtoms.ts new file mode 100644 index 000000000..eb2d7c3f1 --- /dev/null +++ b/frontend/src/modules/DistributionPlot/settings/atoms/baseAtoms.ts @@ -0,0 +1,7 @@ +import { PlotType } from "@modules/DistributionPlot/typesAndEnums"; + +import { atom } from "jotai"; + +export const plotTypeAtom = atom(PlotType.Histogram); +export const numBinsAtom = atom(10); +export const orientationAtom = atom<"h" | "v">("h"); diff --git a/frontend/src/modules/DistributionPlot/settings.tsx b/frontend/src/modules/DistributionPlot/settings/settings.tsx similarity index 88% rename from frontend/src/modules/DistributionPlot/settings.tsx rename to frontend/src/modules/DistributionPlot/settings/settings.tsx index 1109082d2..821b657ad 100644 --- a/frontend/src/modules/DistributionPlot/settings.tsx +++ b/frontend/src/modules/DistributionPlot/settings/settings.tsx @@ -8,7 +8,12 @@ import { Label } from "@lib/components/Label"; import { RadioGroup } from "@lib/components/RadioGroup"; import { Slider } from "@lib/components/Slider"; -import { PlotType, State } from "./state"; +import { useAtom } from "jotai"; + +import { numBinsAtom, orientationAtom, plotTypeAtom } from "./atoms/baseAtoms"; + +import { Interfaces } from "../interfaces"; +import { PlotType } from "../typesAndEnums"; const plotTypes = [ { @@ -30,10 +35,10 @@ const plotTypes = [ ]; //----------------------------------------------------------------------------------------------------------- -export function Settings({ settingsContext, initialSettings }: ModuleSettingsProps) { - const [plotType, setPlotType] = settingsContext.useStoreState("plotType"); - const [numBins, setNumBins] = settingsContext.useStoreState("numBins"); - const [orientation, setOrientation] = settingsContext.useStoreState("orientation"); +export function Settings({ initialSettings }: ModuleSettingsProps) { + const [plotType, setPlotType] = useAtom(plotTypeAtom); + const [numBins, setNumBins] = useAtom(numBinsAtom); + const [orientation, setOrientation] = useAtom(orientationAtom); useApplyInitialSettingsToState(initialSettings, "plotType", "string", setPlotType); useApplyInitialSettingsToState(initialSettings, "numBins", "number", setNumBins); diff --git a/frontend/src/modules/DistributionPlot/state.ts b/frontend/src/modules/DistributionPlot/typesAndEnums.ts similarity index 59% rename from frontend/src/modules/DistributionPlot/state.ts rename to frontend/src/modules/DistributionPlot/typesAndEnums.ts index 5f108da5d..ed220cbd4 100644 --- a/frontend/src/modules/DistributionPlot/state.ts +++ b/frontend/src/modules/DistributionPlot/typesAndEnums.ts @@ -4,9 +4,3 @@ export enum PlotType { Scatter = "scatter", ScatterWithColorMapping = "scatterWithColor", } - -export interface State { - plotType: PlotType | null; - numBins: number; - orientation: "v" | "h"; -} diff --git a/frontend/src/modules/DistributionPlot/view.tsx b/frontend/src/modules/DistributionPlot/view.tsx index 7bfc7b1c5..ae359eea5 100644 --- a/frontend/src/modules/DistributionPlot/view.tsx +++ b/frontend/src/modules/DistributionPlot/view.tsx @@ -14,7 +14,8 @@ import { Warning } from "@mui/icons-material"; import { Layout, PlotData } from "plotly.js"; -import { PlotType, State } from "./state"; +import { Interfaces } from "./interfaces"; +import { PlotType } from "./typesAndEnums"; import { makeHistogramTrace } from "./utils/histogram"; import { makeHoverText, makeHoverTextWithColor, makeTitleFromChannelContent } from "./utils/stringUtils"; import { calcTextSize } from "./utils/textSize"; @@ -33,7 +34,7 @@ const MaxNumberPlotsExceededMessage: React.FC = () => { MaxNumberPlotsExceededMessage.displayName = "MaxNumberPlotsExceededMessage"; -export const View = ({ viewContext, workbenchSettings }: ModuleViewProps) => { +export const View = ({ viewContext, workbenchSettings }: ModuleViewProps) => { const [isPending, startTransition] = React.useTransition(); const [content, setContent] = React.useState(null); const [revNumberX, setRevNumberX] = React.useState(0); @@ -44,9 +45,9 @@ export const View = ({ viewContext, workbenchSettings }: ModuleViewProps) const [prevOrientation, setPrevOrientation] = React.useState<"v" | "h" | null>(null); const [prevSize, setPrevSize] = React.useState(null); - const plotType = viewContext.useStoreValue("plotType"); - const numBins = viewContext.useStoreValue("numBins"); - const orientation = viewContext.useStoreValue("orientation"); + const plotType = viewContext.useSettingsToViewInterfaceValue("plotType"); + const numBins = viewContext.useSettingsToViewInterfaceValue("numBins"); + const orientation = viewContext.useSettingsToViewInterfaceValue("orientation"); const statusWriter = useViewStatusWriter(viewContext); diff --git a/frontend/src/modules/FlowNetwork/settingsToViewInterface.ts b/frontend/src/modules/FlowNetwork/interfaces.ts similarity index 85% rename from frontend/src/modules/FlowNetwork/settingsToViewInterface.ts rename to frontend/src/modules/FlowNetwork/interfaces.ts index 5c3c77d44..cf0193239 100644 --- a/frontend/src/modules/FlowNetwork/settingsToViewInterface.ts +++ b/frontend/src/modules/FlowNetwork/interfaces.ts @@ -12,9 +12,7 @@ import { } from "./settings/atoms/derivedAtoms"; import { QueryStatus } from "./types"; -export type State = Record; - -export type Interface = { +type SettingsToViewInterface = { edgeMetadataList: EdgeMetadata[]; nodeMetadataList: NodeMetadata[]; datedTrees: DatedTree[]; @@ -24,7 +22,11 @@ export type Interface = { queryStatus: QueryStatus; }; -export const interfaceInitialization: InterfaceInitialization = { +export type Interfaces = { + settingsToView: SettingsToViewInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { edgeMetadataList: (get) => { return get(edgeMetadataListAtom); }, diff --git a/frontend/src/modules/FlowNetwork/loadModule.tsx b/frontend/src/modules/FlowNetwork/loadModule.tsx index f9f54611c..28c1dbd3c 100644 --- a/frontend/src/modules/FlowNetwork/loadModule.tsx +++ b/frontend/src/modules/FlowNetwork/loadModule.tsx @@ -1,13 +1,11 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; +import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; import { MODULE_NAME } from "./registerModule"; import { Settings } from "./settings/settings"; -import { Interface, State, interfaceInitialization } from "./settingsToViewInterface"; import { View } from "./view"; -const defaultState: State = {}; - -const module = ModuleRegistry.initModule(MODULE_NAME, defaultState, {}, interfaceInitialization); +const module = ModuleRegistry.initModule(MODULE_NAME, { settingsToViewInterfaceInitialization }); module.viewFC = View; module.settingsFC = Settings; diff --git a/frontend/src/modules/FlowNetwork/registerModule.ts b/frontend/src/modules/FlowNetwork/registerModule.ts index 3611a74db..05eabcffc 100644 --- a/frontend/src/modules/FlowNetwork/registerModule.ts +++ b/frontend/src/modules/FlowNetwork/registerModule.ts @@ -2,14 +2,14 @@ import { ModuleCategory, ModuleDevState } from "@framework/Module"; import { ModuleDataTagId } from "@framework/ModuleDataTags"; import { ModuleRegistry } from "@framework/ModuleRegistry"; +import { Interfaces } from "./interfaces"; import { preview } from "./preview"; -import { Interface, State } from "./settingsToViewInterface"; export const MODULE_NAME = "FlowNetwork"; const description = "Visualizes dated group trees over time."; -ModuleRegistry.registerModule({ +ModuleRegistry.registerModule({ moduleName: MODULE_NAME, defaultTitle: "Flow Network", category: ModuleCategory.MAIN, diff --git a/frontend/src/modules/FlowNetwork/settings/settings.tsx b/frontend/src/modules/FlowNetwork/settings/settings.tsx index 63159bd82..056f2c959 100644 --- a/frontend/src/modules/FlowNetwork/settings/settings.tsx +++ b/frontend/src/modules/FlowNetwork/settings/settings.tsx @@ -39,10 +39,10 @@ import { selectedRealizationNumberAtom, } from "./atoms/derivedAtoms"; -import { Interface, State } from "../settingsToViewInterface"; +import { Interfaces } from "../interfaces"; import { FrequencyEnumToStringMapping, NodeTypeEnumToStringMapping } from "../types"; -export function Settings({ workbenchSession, settingsContext }: ModuleSettingsProps) { +export function Settings({ workbenchSession, settingsContext }: ModuleSettingsProps) { const ensembleSet = useEnsembleSet(workbenchSession); const statusWriter = useSettingsStatusWriter(settingsContext); diff --git a/frontend/src/modules/FlowNetwork/view.tsx b/frontend/src/modules/FlowNetwork/view.tsx index f24b46ee0..5f8fa8e1b 100644 --- a/frontend/src/modules/FlowNetwork/view.tsx +++ b/frontend/src/modules/FlowNetwork/view.tsx @@ -4,10 +4,10 @@ import { CircularProgress } from "@lib/components/CircularProgress"; import { ContentError, ContentInfo } from "@modules/_shared/components/ContentMessage"; import { GroupTreePlot } from "@webviz/group-tree-plot"; -import { Interface, State } from "./settingsToViewInterface"; +import { Interfaces } from "./interfaces"; import { QueryStatus } from "./types"; -export function View({ viewContext }: ModuleViewProps) { +export function View({ viewContext }: ModuleViewProps) { const edgeMetadataList = viewContext.useSettingsToViewInterfaceValue("edgeMetadataList"); const nodeMetadataList = viewContext.useSettingsToViewInterfaceValue("nodeMetadataList"); const datedTrees = viewContext.useSettingsToViewInterfaceValue("datedTrees"); diff --git a/frontend/src/modules/InplaceVolumetrics/interfaces.ts b/frontend/src/modules/InplaceVolumetrics/interfaces.ts new file mode 100644 index 000000000..b886daa1b --- /dev/null +++ b/frontend/src/modules/InplaceVolumetrics/interfaces.ts @@ -0,0 +1,34 @@ +import { InplaceVolumetricsCategoricalMetaData_api } from "@api"; +import { EnsembleIdent } from "@framework/EnsembleIdent"; +import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; + +import { + categoricalFilterAtom, + categoricalOptionsAtom, + ensembleIdentAtom, + realizationsToIncludeAtom, + responseNameAtom, + tableNameAtom, +} from "./settings/atoms/baseAtoms"; + +type SettingsToViewInterface = { + ensembleIdent: EnsembleIdent | null; + tableName: string | null; + responseName: string | null; + categoricalOptions: InplaceVolumetricsCategoricalMetaData_api[] | null; + categoricalFilter: InplaceVolumetricsCategoricalMetaData_api[] | null; + realizationsToInclude: number[] | null; +}; + +export type Interfaces = { + settingsToView: SettingsToViewInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { + ensembleIdent: (get) => get(ensembleIdentAtom), + tableName: (get) => get(tableNameAtom), + responseName: (get) => get(responseNameAtom), + categoricalOptions: (get) => get(categoricalOptionsAtom), + categoricalFilter: (get) => get(categoricalFilterAtom), + realizationsToInclude: (get) => get(realizationsToIncludeAtom), +}; diff --git a/frontend/src/modules/InplaceVolumetrics/loadModule.tsx b/frontend/src/modules/InplaceVolumetrics/loadModule.tsx index 703c8fbe3..8cdb4582d 100644 --- a/frontend/src/modules/InplaceVolumetrics/loadModule.tsx +++ b/frontend/src/modules/InplaceVolumetrics/loadModule.tsx @@ -1,19 +1,10 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; -import { Settings } from "./settings"; -import { State } from "./state"; +import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; +import { Settings } from "./settings/settings"; import { View } from "./view"; -const defaultState: State = { - ensembleIdent: null, - tableName: null, - categoricalOptions: null, - categoricalFilter: null, - responseName: null, - realizationsToInclude: null, -}; - -const module = ModuleRegistry.initModule("InplaceVolumetrics", defaultState); +const module = ModuleRegistry.initModule("InplaceVolumetrics", { settingsToViewInterfaceInitialization }); module.viewFC = View; module.settingsFC = Settings; diff --git a/frontend/src/modules/InplaceVolumetrics/registerModule.ts b/frontend/src/modules/InplaceVolumetrics/registerModule.ts index ba03da06a..3ba0a070a 100644 --- a/frontend/src/modules/InplaceVolumetrics/registerModule.ts +++ b/frontend/src/modules/InplaceVolumetrics/registerModule.ts @@ -3,11 +3,11 @@ import { ModuleDataTagId } from "@framework/ModuleDataTags"; import { ModuleRegistry } from "@framework/ModuleRegistry"; import { channelDefs } from "./channelDefs"; -import { State } from "./state"; +import { Interfaces } from "./interfaces"; const description = "Plotting of in-place volumetric distributions."; -ModuleRegistry.registerModule({ +ModuleRegistry.registerModule({ moduleName: "InplaceVolumetrics", defaultTitle: "Inplace volumetrics", channelDefinitions: channelDefs, diff --git a/frontend/src/modules/InplaceVolumetrics/settings/atoms/baseAtoms.ts b/frontend/src/modules/InplaceVolumetrics/settings/atoms/baseAtoms.ts new file mode 100644 index 000000000..deefc0d39 --- /dev/null +++ b/frontend/src/modules/InplaceVolumetrics/settings/atoms/baseAtoms.ts @@ -0,0 +1,11 @@ +import { InplaceVolumetricsCategoricalMetaData_api } from "@api"; +import { EnsembleIdent } from "@framework/EnsembleIdent"; + +import { atom } from "jotai"; + +export const ensembleIdentAtom = atom(null); +export const tableNameAtom = atom(null); +export const responseNameAtom = atom(null); +export const categoricalOptionsAtom = atom(null); +export const categoricalFilterAtom = atom(null); +export const realizationsToIncludeAtom = atom(null); diff --git a/frontend/src/modules/InplaceVolumetrics/settings.tsx b/frontend/src/modules/InplaceVolumetrics/settings/settings.tsx similarity index 93% rename from frontend/src/modules/InplaceVolumetrics/settings.tsx rename to frontend/src/modules/InplaceVolumetrics/settings/settings.tsx index a115b5ed6..53fdfe45c 100644 --- a/frontend/src/modules/InplaceVolumetrics/settings.tsx +++ b/frontend/src/modules/InplaceVolumetrics/settings/settings.tsx @@ -13,8 +13,12 @@ import { QueryStateWrapper } from "@lib/components/QueryStateWrapper"; import { Select } from "@lib/components/Select"; import { UseQueryResult } from "@tanstack/react-query"; -import { useTableDescriptionsQuery } from "./queryHooks"; -import { State } from "./state"; +import { useAtom } from "jotai"; + +import { categoricalFilterAtom, ensembleIdentAtom, responseNameAtom, tableNameAtom } from "./atoms/baseAtoms"; + +import { Interfaces } from "../interfaces"; +import { useTableDescriptionsQuery } from "../queryHooks"; //----------------------------------------------------------------------------------------------------------- @@ -96,12 +100,12 @@ function getTableResponseOptions( return responsesToSelectOptions(responses); } -export function Settings({ settingsContext, workbenchSession }: ModuleSettingsProps) { +export function Settings({ workbenchSession }: ModuleSettingsProps) { const ensembleSet = useEnsembleSet(workbenchSession); - const [ensembleIdent, setEnsembleIdent] = settingsContext.useStoreState("ensembleIdent"); - const [tableName, setTableName] = settingsContext.useStoreState("tableName"); - const [categoricalFilter, setCategoricalFilter] = settingsContext.useStoreState("categoricalFilter"); - const [responseName, setResponseName] = settingsContext.useStoreState("responseName"); + const [ensembleIdent, setEnsembleIdent] = useAtom(ensembleIdentAtom); + const [tableName, setTableName] = useAtom(tableNameAtom); + const [categoricalFilter, setCategoricalFilter] = useAtom(categoricalFilterAtom); + const [responseName, setResponseName] = useAtom(responseNameAtom); const tableDescriptionsQuery = useTableDescriptionsQuery(ensembleIdent, true); diff --git a/frontend/src/modules/InplaceVolumetrics/state.ts b/frontend/src/modules/InplaceVolumetrics/state.ts deleted file mode 100644 index 7fee42129..000000000 --- a/frontend/src/modules/InplaceVolumetrics/state.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { InplaceVolumetricsCategoricalMetaData_api } from "@api"; -import { EnsembleIdent } from "@framework/EnsembleIdent"; - -export interface State { - ensembleIdent: EnsembleIdent | null; - tableName: string | null; - responseName: string | null; - categoricalOptions: InplaceVolumetricsCategoricalMetaData_api[] | null; - categoricalFilter: InplaceVolumetricsCategoricalMetaData_api[] | null; - realizationsToInclude: number[] | null; -} diff --git a/frontend/src/modules/InplaceVolumetrics/view.tsx b/frontend/src/modules/InplaceVolumetrics/view.tsx index 292ffd8a0..4db6b7a12 100644 --- a/frontend/src/modules/InplaceVolumetrics/view.tsx +++ b/frontend/src/modules/InplaceVolumetrics/view.tsx @@ -12,17 +12,17 @@ import { useElementSize } from "@lib/hooks/useElementSize"; import { Layout, PlotData, PlotHoverEvent } from "plotly.js"; import { ChannelIds } from "./channelDefs"; +import { Interfaces } from "./interfaces"; import { useRealizationsResponseQuery } from "./queryHooks"; -import { VolumetricResponseAbbreviations } from "./settings"; -import { State } from "./state"; +import { VolumetricResponseAbbreviations } from "./settings/settings"; -export const View = (props: ModuleViewProps) => { +export const View = (props: ModuleViewProps) => { const wrapperDivRef = React.useRef(null); const wrapperDivSize = useElementSize(wrapperDivRef); - const ensembleIdent = props.viewContext.useStoreValue("ensembleIdent"); - const tableName = props.viewContext.useStoreValue("tableName"); - const responseName = props.viewContext.useStoreValue("responseName"); - const categoryFilter = props.viewContext.useStoreValue("categoricalFilter"); + const ensembleIdent = props.viewContext.useSettingsToViewInterfaceValue("ensembleIdent"); + const tableName = props.viewContext.useSettingsToViewInterfaceValue("tableName"); + const responseName = props.viewContext.useSettingsToViewInterfaceValue("responseName"); + const categoryFilter = props.viewContext.useSettingsToViewInterfaceValue("categoricalFilter"); const responseBody: Body_get_realizations_response_api = { categorical_filter: categoryFilter || undefined }; const realizationsResponseQuery = useRealizationsResponseQuery( ensembleIdent?.getCaseUuid() ?? "", @@ -95,7 +95,7 @@ export const View = (props: ModuleViewProps) => { dependencies: [realizationsResponseQuery.data, ensemble, tableName, responseName], contents: [{ contentIdString: responseName || "", displayName: responseName || "", dataGenerator }], }); - + const layout: Partial = { width: wrapperDivSize.width, height: wrapperDivSize.height, diff --git a/frontend/src/modules/Intersection/settingsToViewInterface.ts b/frontend/src/modules/Intersection/interfaces.ts similarity index 88% rename from frontend/src/modules/Intersection/settingsToViewInterface.ts rename to frontend/src/modules/Intersection/interfaces.ts index 47b4de98e..ccac90da8 100644 --- a/frontend/src/modules/Intersection/settingsToViewInterface.ts +++ b/frontend/src/modules/Intersection/interfaces.ts @@ -18,6 +18,7 @@ import { selectedEnsembleIdentAtom, selectedWellboreAtom, } from "./settings/atoms/derivedAtoms"; +import { WellboreHeader } from "./typesAndEnums"; import { LayerManager } from "./utils/layers/LayerManager"; export type SettingsToViewInterface = { @@ -31,15 +32,14 @@ export type SettingsToViewInterface = { ensembleIdent: EnsembleIdent | null; selectedCustomIntersectionPolylineId: string | null; layerManager: LayerManager; - wellboreHeader: { - uuid: string; - identifier: string; - depthReferencePoint: string; - depthReferenceElevation: number; - } | null; + wellboreHeader: WellboreHeader | null; }; -export const interfaceInitialization: InterfaceInitialization = { +export type Interfaces = { + settingsToView: SettingsToViewInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { showGridlines: (get) => { return get(showGridlinesAtom); }, diff --git a/frontend/src/modules/Intersection/loadModule.tsx b/frontend/src/modules/Intersection/loadModule.tsx index ad87fc7f9..914eb2dd8 100644 --- a/frontend/src/modules/Intersection/loadModule.tsx +++ b/frontend/src/modules/Intersection/loadModule.tsx @@ -1,20 +1,15 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; +import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; import { MODULE_NAME } from "./registerModule"; import { Settings } from "./settings/settings"; -import { SettingsToViewInterface, interfaceInitialization } from "./settingsToViewInterface"; -import { State } from "./state"; -import { ViewAtoms, viewAtomsInitialization } from "./view/atoms/atomDefinitions"; +import { settingsToViewInterfaceEffects } from "./view/atoms/interfaceEffects"; import { View } from "./view/view"; -const module = ModuleRegistry.initModule, ViewAtoms>( - MODULE_NAME, - {}, - {}, - interfaceInitialization, - undefined, - viewAtomsInitialization -); +const module = ModuleRegistry.initModule(MODULE_NAME, { + settingsToViewInterfaceInitialization, + settingsToViewInterfaceEffects, +}); module.viewFC = View; module.settingsFC = Settings; diff --git a/frontend/src/modules/Intersection/registerModule.ts b/frontend/src/modules/Intersection/registerModule.ts index e6093f70b..6e08a5446 100644 --- a/frontend/src/modules/Intersection/registerModule.ts +++ b/frontend/src/modules/Intersection/registerModule.ts @@ -2,14 +2,12 @@ import { ModuleCategory, ModuleDevState } from "@framework/Module"; import { ModuleRegistry } from "@framework/ModuleRegistry"; import { SyncSettingKey } from "@framework/SyncSettings"; +import { Interfaces } from "./interfaces"; import { preview } from "./preview"; -import { SettingsToViewInterface } from "./settingsToViewInterface"; -import { State } from "./state"; -import { ViewAtoms } from "./view/atoms/atomDefinitions"; export const MODULE_NAME = "Intersection"; -ModuleRegistry.registerModule, ViewAtoms>({ +ModuleRegistry.registerModule({ moduleName: MODULE_NAME, defaultTitle: "Intersection", category: ModuleCategory.MAIN, diff --git a/frontend/src/modules/Intersection/settings/settings.tsx b/frontend/src/modules/Intersection/settings/settings.tsx index 65d1a6f57..b72f8b249 100644 --- a/frontend/src/modules/Intersection/settings/settings.tsx +++ b/frontend/src/modules/Intersection/settings/settings.tsx @@ -38,13 +38,9 @@ import { import { drilledWellboreHeadersQueryAtom } from "./atoms/queryAtoms"; import { Layers } from "./components/layers"; -import { SettingsToViewInterface } from "../settingsToViewInterface"; -import { State } from "../state"; -import { ViewAtoms } from "../view/atoms/atomDefinitions"; +import { Interfaces } from "../interfaces"; -export function Settings( - props: ModuleSettingsProps, ViewAtoms> -): JSX.Element { +export function Settings(props: ModuleSettingsProps): JSX.Element { const ensembleSet = useEnsembleSet(props.workbenchSession); const filteredEnsembleSet = useAtomValue(filteredEnsembleSetAtom); const statusWriter = useSettingsStatusWriter(props.settingsContext); diff --git a/frontend/src/modules/Intersection/state.ts b/frontend/src/modules/Intersection/state.ts deleted file mode 100644 index 90ff7f709..000000000 --- a/frontend/src/modules/Intersection/state.ts +++ /dev/null @@ -1 +0,0 @@ -export type State = Record; diff --git a/frontend/src/modules/Intersection/typesAndEnums.ts b/frontend/src/modules/Intersection/typesAndEnums.ts index b08010c66..f383bd50f 100644 --- a/frontend/src/modules/Intersection/typesAndEnums.ts +++ b/frontend/src/modules/Intersection/typesAndEnums.ts @@ -1 +1,8 @@ export const CURVE_FITTING_EPSILON = 5; // meter + +export type WellboreHeader = { + uuid: string; + identifier: string; + depthReferencePoint: string; + depthReferenceElevation: number; +}; diff --git a/frontend/src/modules/Intersection/view/atoms/atomDefinitions.ts b/frontend/src/modules/Intersection/view/atoms/atomDefinitions.ts deleted file mode 100644 index 68fd1145b..000000000 --- a/frontend/src/modules/Intersection/view/atoms/atomDefinitions.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { WellboreTrajectory_api } from "@api"; -import { IntersectionReferenceSystem } from "@equinor/esv-intersection"; -import { apiService } from "@framework/ApiService"; -import { ModuleAtoms } from "@framework/Module"; -import { UniDirectionalModuleComponentsInterface } from "@framework/UniDirectionalModuleComponentsInterface"; -import { IntersectionType } from "@framework/types/intersection"; -import { IntersectionPolylinesAtom } from "@framework/userCreatedItems/IntersectionPolylines"; -import { point2Distance, vec2FromArray } from "@lib/utils/vec2"; -import { SettingsToViewInterface } from "@modules/Intersection/settingsToViewInterface"; -import { CURVE_FITTING_EPSILON } from "@modules/Intersection/typesAndEnums"; -import { calcExtendedSimplifiedWellboreTrajectoryInXYPlane } from "@modules/_shared/utils/wellbore"; -import { QueryObserverResult } from "@tanstack/react-query"; - -import { atom } from "jotai"; -import { atomWithQuery } from "jotai-tanstack-query"; - -const STALE_TIME = 60 * 1000; -const CACHE_TIME = 60 * 1000; - -export type ViewAtoms = { - intersectionReferenceSystemAtom: IntersectionReferenceSystem | null; - polylineAtom: { - polylineUtmXy: number[]; - actualSectionLengths: number[]; - }; - wellboreTrajectoryQueryAtom: QueryObserverResult; -}; - -export function viewAtomsInitialization( - settingsToViewInterface: UniDirectionalModuleComponentsInterface -): ModuleAtoms { - const selectedCustomIntersectionPolylineAtom = atom((get) => { - const customIntersectionPolylineId = get( - settingsToViewInterface.getAtom("selectedCustomIntersectionPolylineId") - ); - const customIntersectionPolylines = get(IntersectionPolylinesAtom); - - return customIntersectionPolylines.find((el) => el.id === customIntersectionPolylineId); - }); - - const intersectionReferenceSystemAtom = atom((get) => { - const wellboreTrajectoryQuery = get(wellboreTrajectoryQueryAtom); - const customIntersectionPolyline = get(selectedCustomIntersectionPolylineAtom); - const intersectionType = get(settingsToViewInterface.getAtom("intersectionType")); - - if (intersectionType === IntersectionType.WELLBORE) { - if (!wellboreTrajectoryQuery.data) { - return null; - } - - const wellboreTrajectory = wellboreTrajectoryQuery.data; - - if (wellboreTrajectoryQuery) { - const path: number[][] = []; - for (const [index, northing] of wellboreTrajectory.northingArr.entries()) { - const easting = wellboreTrajectory.eastingArr[index]; - const tvd_msl = wellboreTrajectory.tvdMslArr[index]; - - path.push([easting, northing, tvd_msl]); - } - const offset = wellboreTrajectory.mdArr[0]; - - const referenceSystem = new IntersectionReferenceSystem(path); - referenceSystem.offset = offset; - - return referenceSystem; - } - } else if (intersectionType === IntersectionType.CUSTOM_POLYLINE && customIntersectionPolyline) { - if (customIntersectionPolyline.points.length < 2) { - return null; - } - const referenceSystem = new IntersectionReferenceSystem( - customIntersectionPolyline.points.map((point) => [point[0], point[1], 0]) - ); - referenceSystem.offset = 0; - - return referenceSystem; - } - - return null; - }); - - const polylineAtom = atom((get) => { - const intersectionType = get(settingsToViewInterface.getAtom("intersectionType")); - const intersectionExtensionLength = get(settingsToViewInterface.getAtom("intersectionExtensionLength")); - const selectedCustomIntersectionPolyline = get(selectedCustomIntersectionPolylineAtom); - const intersectionReferenceSystem = get(intersectionReferenceSystemAtom); - - const polylineUtmXy: number[] = []; - const actualSectionLengths: number[] = []; - - if (intersectionReferenceSystem) { - if (intersectionType === IntersectionType.WELLBORE) { - const path = intersectionReferenceSystem.path; - const simplifiedCurveResult = calcExtendedSimplifiedWellboreTrajectoryInXYPlane( - path, - intersectionExtensionLength, - CURVE_FITTING_EPSILON - ); - polylineUtmXy.push(...simplifiedCurveResult.simplifiedWellboreTrajectoryXy.flat()); - actualSectionLengths.push(...simplifiedCurveResult.actualSectionLengths); - } else if (intersectionType === IntersectionType.CUSTOM_POLYLINE && selectedCustomIntersectionPolyline) { - for (const [index, point] of selectedCustomIntersectionPolyline.points.entries()) { - polylineUtmXy.push(point[0], point[1]); - if (index > 0) { - const previousPoint = selectedCustomIntersectionPolyline.points[index - 1]; - actualSectionLengths.push(point2Distance(vec2FromArray(point), vec2FromArray(previousPoint))); - } - } - } - } - - return { - polylineUtmXy, - actualSectionLengths, - }; - }); - - const wellboreTrajectoryQueryAtom = atomWithQuery((get) => { - const wellbore = get(settingsToViewInterface.getAtom("wellboreHeader")); - - return { - queryKey: ["getWellboreTrajectory", wellbore?.uuid ?? ""], - queryFn: () => apiService.well.getWellTrajectories(wellbore?.uuid ? [wellbore.uuid] : []), - staleTime: STALE_TIME, - gcTime: CACHE_TIME, - select: (data: WellboreTrajectory_api[]) => data[0], - enabled: wellbore?.uuid ? true : false, - }; - }); - - return { - intersectionReferenceSystemAtom, - polylineAtom, - wellboreTrajectoryQueryAtom, - }; -} diff --git a/frontend/src/modules/Intersection/view/atoms/baseAtoms.ts b/frontend/src/modules/Intersection/view/atoms/baseAtoms.ts new file mode 100644 index 000000000..0e9b3f426 --- /dev/null +++ b/frontend/src/modules/Intersection/view/atoms/baseAtoms.ts @@ -0,0 +1,9 @@ +import { IntersectionType } from "@framework/types/intersection"; +import { WellboreHeader } from "@modules/Intersection/typesAndEnums"; + +import { atom } from "jotai"; + +export const selectedCustomIntersectionPolylineIdAtom = atom(null); +export const intersectionTypeAtom = atom(IntersectionType.WELLBORE); +export const wellboreHeaderAtom = atom(null); +export const intersectionExtensionLengthAtom = atom(1000); diff --git a/frontend/src/modules/Intersection/view/atoms/derivedAtoms.ts b/frontend/src/modules/Intersection/view/atoms/derivedAtoms.ts new file mode 100644 index 000000000..2e19c5dd6 --- /dev/null +++ b/frontend/src/modules/Intersection/view/atoms/derivedAtoms.ts @@ -0,0 +1,100 @@ +import { IntersectionReferenceSystem } from "@equinor/esv-intersection"; +import { IntersectionType } from "@framework/types/intersection"; +import { IntersectionPolylinesAtom } from "@framework/userCreatedItems/IntersectionPolylines"; +import { point2Distance, vec2FromArray } from "@lib/utils/vec2"; +import { CURVE_FITTING_EPSILON } from "@modules/Intersection/typesAndEnums"; +import { calcExtendedSimplifiedWellboreTrajectoryInXYPlane } from "@modules/_shared/utils/wellbore"; + +import { atom } from "jotai"; + +import { + intersectionExtensionLengthAtom, + intersectionTypeAtom, + selectedCustomIntersectionPolylineIdAtom, +} from "./baseAtoms"; +import { wellboreTrajectoryQueryAtom } from "./queryAtoms"; + +export const selectedCustomIntersectionPolylineAtom = atom((get) => { + const customIntersectionPolylineId = get(selectedCustomIntersectionPolylineIdAtom); + const customIntersectionPolylines = get(IntersectionPolylinesAtom); + + return customIntersectionPolylines.find((el) => el.id === customIntersectionPolylineId); +}); + +export const intersectionReferenceSystemAtom = atom((get) => { + const wellboreTrajectoryQuery = get(wellboreTrajectoryQueryAtom); + const customIntersectionPolyline = get(selectedCustomIntersectionPolylineAtom); + const intersectionType = get(intersectionTypeAtom); + + if (intersectionType === IntersectionType.WELLBORE) { + if (!wellboreTrajectoryQuery.data) { + return null; + } + + const wellboreTrajectory = wellboreTrajectoryQuery.data; + + if (wellboreTrajectoryQuery) { + const path: number[][] = []; + for (const [index, northing] of wellboreTrajectory.northingArr.entries()) { + const easting = wellboreTrajectory.eastingArr[index]; + const tvd_msl = wellboreTrajectory.tvdMslArr[index]; + + path.push([easting, northing, tvd_msl]); + } + const offset = wellboreTrajectory.mdArr[0]; + + const referenceSystem = new IntersectionReferenceSystem(path); + referenceSystem.offset = offset; + + return referenceSystem; + } + } else if (intersectionType === IntersectionType.CUSTOM_POLYLINE && customIntersectionPolyline) { + if (customIntersectionPolyline.points.length < 2) { + return null; + } + const referenceSystem = new IntersectionReferenceSystem( + customIntersectionPolyline.points.map((point) => [point[0], point[1], 0]) + ); + referenceSystem.offset = 0; + + return referenceSystem; + } + + return null; +}); + +export const polylineAtom = atom((get) => { + const intersectionType = get(intersectionTypeAtom); + const intersectionExtensionLength = get(intersectionExtensionLengthAtom); + const selectedCustomIntersectionPolyline = get(selectedCustomIntersectionPolylineAtom); + const intersectionReferenceSystem = get(intersectionReferenceSystemAtom); + + const polylineUtmXy: number[] = []; + const actualSectionLengths: number[] = []; + + if (intersectionReferenceSystem) { + if (intersectionType === IntersectionType.WELLBORE) { + const path = intersectionReferenceSystem.path; + const simplifiedCurveResult = calcExtendedSimplifiedWellboreTrajectoryInXYPlane( + path, + intersectionExtensionLength, + CURVE_FITTING_EPSILON + ); + polylineUtmXy.push(...simplifiedCurveResult.simplifiedWellboreTrajectoryXy.flat()); + actualSectionLengths.push(...simplifiedCurveResult.actualSectionLengths); + } else if (intersectionType === IntersectionType.CUSTOM_POLYLINE && selectedCustomIntersectionPolyline) { + for (const [index, point] of selectedCustomIntersectionPolyline.points.entries()) { + polylineUtmXy.push(point[0], point[1]); + if (index > 0) { + const previousPoint = selectedCustomIntersectionPolyline.points[index - 1]; + actualSectionLengths.push(point2Distance(vec2FromArray(point), vec2FromArray(previousPoint))); + } + } + } + } + + return { + polylineUtmXy, + actualSectionLengths, + }; +}); diff --git a/frontend/src/modules/Intersection/view/atoms/interfaceEffects.ts b/frontend/src/modules/Intersection/view/atoms/interfaceEffects.ts new file mode 100644 index 000000000..575de6efa --- /dev/null +++ b/frontend/src/modules/Intersection/view/atoms/interfaceEffects.ts @@ -0,0 +1,28 @@ +import { InterfaceEffects } from "@framework/Module"; +import { SettingsToViewInterface } from "@modules/Intersection/interfaces"; + +import { + intersectionExtensionLengthAtom, + intersectionTypeAtom, + selectedCustomIntersectionPolylineIdAtom, + wellboreHeaderAtom, +} from "./baseAtoms"; + +export const settingsToViewInterfaceEffects: InterfaceEffects = [ + (getInterfaceValue, setAtomValue) => { + const selectedCustomIntersectionPolylineId = getInterfaceValue("selectedCustomIntersectionPolylineId"); + setAtomValue(selectedCustomIntersectionPolylineIdAtom, selectedCustomIntersectionPolylineId); + }, + (getInterfaceValue, setAtomValue) => { + const intersectionType = getInterfaceValue("intersectionType"); + setAtomValue(intersectionTypeAtom, intersectionType); + }, + (getInterfaceValue, setAtomValue) => { + const wellboreHeader = getInterfaceValue("wellboreHeader"); + setAtomValue(wellboreHeaderAtom, wellboreHeader); + }, + (getInterfaceValue, setAtomValue) => { + const intersectionExtensionLength = getInterfaceValue("intersectionExtensionLength"); + setAtomValue(intersectionExtensionLengthAtom, intersectionExtensionLength); + }, +]; diff --git a/frontend/src/modules/Intersection/view/atoms/queryAtoms.ts b/frontend/src/modules/Intersection/view/atoms/queryAtoms.ts new file mode 100644 index 000000000..ca98144d2 --- /dev/null +++ b/frontend/src/modules/Intersection/view/atoms/queryAtoms.ts @@ -0,0 +1,22 @@ +import { WellboreTrajectory_api } from "@api"; +import { apiService } from "@framework/ApiService"; + +import { atomWithQuery } from "jotai-tanstack-query"; + +import { wellboreHeaderAtom } from "./baseAtoms"; + +const STALE_TIME = 60 * 1000; +const CACHE_TIME = 60 * 1000; + +export const wellboreTrajectoryQueryAtom = atomWithQuery((get) => { + const wellbore = get(wellboreHeaderAtom); + + return { + queryKey: ["getWellboreTrajectory", wellbore?.uuid ?? ""], + queryFn: () => apiService.well.getWellTrajectories(wellbore?.uuid ? [wellbore.uuid] : []), + staleTime: STALE_TIME, + gcTime: CACHE_TIME, + select: (data: WellboreTrajectory_api[]) => data[0], + enabled: wellbore?.uuid ? true : false, + }; +}); diff --git a/frontend/src/modules/Intersection/view/components/layersWrapper.tsx b/frontend/src/modules/Intersection/view/components/layersWrapper.tsx index d14f3117b..7bc341c54 100644 --- a/frontend/src/modules/Intersection/view/components/layersWrapper.tsx +++ b/frontend/src/modules/Intersection/view/components/layersWrapper.tsx @@ -18,8 +18,7 @@ import { SurfaceStatisticalFanchart } from "@framework/components/EsvIntersectio import { makeSurfaceStatisticalFanchartFromRealizationSurface } from "@framework/components/EsvIntersection/utils/surfaceStatisticalFancharts"; import { IntersectionType } from "@framework/types/intersection"; import { useElementBoundingRect } from "@lib/hooks/useElementBoundingRect"; -import { SettingsToViewInterface } from "@modules/Intersection/settingsToViewInterface"; -import { State } from "@modules/Intersection/state"; +import { Interfaces } from "@modules/Intersection/interfaces"; import { BaseLayer, LayerStatus, useLayers } from "@modules/Intersection/utils/layers/BaseLayer"; import { GridLayer, isGridLayer } from "@modules/Intersection/utils/layers/GridLayer"; import { SeismicLayer, isSeismicLayer } from "@modules/Intersection/utils/layers/SeismicLayer"; @@ -33,7 +32,6 @@ import { isEqual } from "lodash"; import { ViewportWrapper } from "./viewportWrapper"; import { ColorScaleWithName } from "../../../_shared/utils/ColorScaleWithName"; -import { ViewAtoms } from "../atoms/atomDefinitions"; export type LayersWrapperProps = { referenceSystem: IntersectionReferenceSystem | null; @@ -42,7 +40,7 @@ export type LayersWrapperProps = { intersectionExtensionLength: number; intersectionType: IntersectionType; workbenchServices: WorkbenchServices; - viewContext: ViewContext, ViewAtoms>; + viewContext: ViewContext; wellboreHeaderUuid: string | null; wellboreHeaderDepthReferencePoint: string | null; wellboreHeaderDepthReferenceElevation: number | null; diff --git a/frontend/src/modules/Intersection/view/components/readoutWrapper.tsx b/frontend/src/modules/Intersection/view/components/readoutWrapper.tsx index a6a525329..750c826f8 100644 --- a/frontend/src/modules/Intersection/view/components/readoutWrapper.tsx +++ b/frontend/src/modules/Intersection/view/components/readoutWrapper.tsx @@ -12,13 +12,10 @@ import { import { HighlightItem, HighlightItemShape, ReadoutItem } from "@framework/components/EsvIntersection/types"; import { ReadoutBox } from "@framework/components/EsvIntersection/utilityComponents/ReadoutBox"; import { isWellborepathLayer } from "@framework/components/EsvIntersection/utils/layers"; -import { SettingsToViewInterface } from "@modules/Intersection/settingsToViewInterface"; -import { State } from "@modules/Intersection/state"; +import { Interfaces } from "@modules/Intersection/interfaces"; import { isEqual } from "lodash"; -import { ViewAtoms } from "../atoms/atomDefinitions"; - export type ReadoutWrapperProps = { wellboreHeaderUuid: string | null; showGrid: boolean; @@ -33,7 +30,7 @@ export type ReadoutWrapperProps = { }; verticalScale: number; workbenchServices: WorkbenchServices; - viewContext: ViewContext, ViewAtoms>; + viewContext: ViewContext; }; export function ReadoutWrapper(props: ReadoutWrapperProps): React.ReactNode { diff --git a/frontend/src/modules/Intersection/view/components/viewportWrapper.tsx b/frontend/src/modules/Intersection/view/components/viewportWrapper.tsx index 85b7bf7f4..95e13736b 100644 --- a/frontend/src/modules/Intersection/view/components/viewportWrapper.tsx +++ b/frontend/src/modules/Intersection/view/components/viewportWrapper.tsx @@ -6,15 +6,12 @@ import { SyncSettingKey, SyncSettingsHelper } from "@framework/SyncSettings"; import { WorkbenchServices } from "@framework/WorkbenchServices"; import { LayerItem, Viewport } from "@framework/components/EsvIntersection"; import { Toolbar } from "@framework/components/EsvIntersection/utilityComponents/Toolbar"; -import { SettingsToViewInterface } from "@modules/Intersection/settingsToViewInterface"; -import { State } from "@modules/Intersection/state"; +import { Interfaces } from "@modules/Intersection/interfaces"; import { cloneDeep, isEqual } from "lodash"; import { ReadoutWrapper } from "./readoutWrapper"; -import { ViewAtoms } from "../atoms/atomDefinitions"; - export type ViewportWrapperProps = { wellboreHeaderUuid: string | null; referenceSystem?: IntersectionReferenceSystem; @@ -26,7 +23,7 @@ export type ViewportWrapperProps = { }; viewport: Viewport | null; workbenchServices: WorkbenchServices; - viewContext: ViewContext, ViewAtoms>; + viewContext: ViewContext; }; export function ViewportWrapper(props: ViewportWrapperProps): React.ReactNode { diff --git a/frontend/src/modules/Intersection/view/view.tsx b/frontend/src/modules/Intersection/view/view.tsx index bdd9583c6..86161141d 100644 --- a/frontend/src/modules/Intersection/view/view.tsx +++ b/frontend/src/modules/Intersection/view/view.tsx @@ -7,12 +7,14 @@ import { IntersectionType } from "@framework/types/intersection"; import { CircularProgress } from "@lib/components/CircularProgress"; import { resolveClassNames } from "@lib/utils/resolveClassNames"; -import { ViewAtoms } from "./atoms/atomDefinitions"; +import { useAtomValue } from "jotai"; + +import { intersectionReferenceSystemAtom, polylineAtom } from "./atoms/derivedAtoms"; +import { wellboreTrajectoryQueryAtom } from "./atoms/queryAtoms"; import { LayersWrapper } from "./components/layersWrapper"; import { useWellboreCasingsQuery } from "./queries/wellboreSchematicsQueries"; -import { SettingsToViewInterface } from "../settingsToViewInterface"; -import { State } from "../state"; +import { Interfaces } from "../interfaces"; import { LayerStatus, useLayersStatuses } from "../utils/layers/BaseLayer"; import { isGridLayer } from "../utils/layers/GridLayer"; import { LayerManagerTopic, useLayerManagerTopicValue } from "../utils/layers/LayerManager"; @@ -21,17 +23,15 @@ import { isSurfaceLayer } from "../utils/layers/SurfaceLayer"; import { isSurfacesUncertaintyLayer } from "../utils/layers/SurfacesUncertaintyLayer"; import { isWellpicksLayer } from "../utils/layers/WellpicksLayer"; -export function View( - props: ModuleViewProps, ViewAtoms> -): JSX.Element { +export function View(props: ModuleViewProps): React.ReactNode { const statusWriter = useViewStatusWriter(props.viewContext); const ensembleSet = useEnsembleSet(props.workbenchSession); const ensembleIdent = props.viewContext.useSettingsToViewInterfaceValue("ensembleIdent"); - const intersectionReferenceSystem = props.viewContext.useViewAtomValue("intersectionReferenceSystemAtom"); + const intersectionReferenceSystem = useAtomValue(intersectionReferenceSystemAtom); const wellboreHeader = props.viewContext.useSettingsToViewInterfaceValue("wellboreHeader"); - const wellboreTrajectoryQuery = props.viewContext.useViewAtomValue("wellboreTrajectoryQueryAtom"); - const polyline = props.viewContext.useViewAtomValue("polylineAtom"); + const wellboreTrajectoryQuery = useAtomValue(wellboreTrajectoryQueryAtom); + const polyline = useAtomValue(polylineAtom); const extensionLength = props.viewContext.useSettingsToViewInterfaceValue("intersectionExtensionLength"); const wellbore = props.viewContext.useSettingsToViewInterfaceValue("wellboreHeader"); diff --git a/frontend/src/modules/Map/MapState.ts b/frontend/src/modules/Map/MapState.ts deleted file mode 100644 index 630749b84..000000000 --- a/frontend/src/modules/Map/MapState.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { FullSurfaceAddress } from "@modules/_shared/Surface"; - -export interface MapState { - surfaceAddress: FullSurfaceAddress | null; -} diff --git a/frontend/src/modules/Map/interfaces.ts b/frontend/src/modules/Map/interfaces.ts new file mode 100644 index 000000000..c951678dc --- /dev/null +++ b/frontend/src/modules/Map/interfaces.ts @@ -0,0 +1,16 @@ +import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; +import { FullSurfaceAddress } from "@modules/_shared/Surface/surfaceAddress"; + +import { surfaceAddressAtom } from "./settings/atoms/baseAtoms"; + +type SettingsToViewInterface = { + surfaceAddress: FullSurfaceAddress | null; +}; + +export type Interfaces = { + settingsToView: SettingsToViewInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { + surfaceAddress: (get) => get(surfaceAddressAtom), +}; diff --git a/frontend/src/modules/Map/loadModule.tsx b/frontend/src/modules/Map/loadModule.tsx index 71946f1bf..e9296f6c4 100644 --- a/frontend/src/modules/Map/loadModule.tsx +++ b/frontend/src/modules/Map/loadModule.tsx @@ -1,16 +1,10 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; -import { MapSettings } from "./MapSettings"; -import { MapState } from "./MapState"; -import { MapView } from "./MapView"; +import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; +import { MapSettings } from "./settings/settings"; +import { MapView } from "./view"; -const defaultState: MapState = { - surfaceAddress: null, -}; - -const module = ModuleRegistry.initModule("Map", defaultState, { - surfaceAddress: { deepCompare: true }, -}); +const module = ModuleRegistry.initModule("Map", { settingsToViewInterfaceInitialization }); module.viewFC = MapView; module.settingsFC = MapSettings; diff --git a/frontend/src/modules/Map/registerModule.ts b/frontend/src/modules/Map/registerModule.ts index 612894c96..00cc0ce42 100644 --- a/frontend/src/modules/Map/registerModule.ts +++ b/frontend/src/modules/Map/registerModule.ts @@ -3,12 +3,12 @@ import { ModuleDataTagId } from "@framework/ModuleDataTags"; import { ModuleRegistry } from "@framework/ModuleRegistry"; import { SyncSettingKey } from "@framework/SyncSettings"; -import { MapState } from "./MapState"; +import { Interfaces } from "./interfaces"; import { preview } from "./preview"; const description = "Plotting of surfaces in a 2D top view."; -ModuleRegistry.registerModule({ +ModuleRegistry.registerModule({ moduleName: "Map", defaultTitle: "Map", category: ModuleCategory.MAIN, diff --git a/frontend/src/modules/Map/settings/atoms/baseAtoms.ts b/frontend/src/modules/Map/settings/atoms/baseAtoms.ts new file mode 100644 index 000000000..2c4afe992 --- /dev/null +++ b/frontend/src/modules/Map/settings/atoms/baseAtoms.ts @@ -0,0 +1,5 @@ +import { FullSurfaceAddress } from "@modules/_shared/Surface"; + +import { atom } from "jotai"; + +export const surfaceAddressAtom = atom(null); diff --git a/frontend/src/modules/Map/MapSettings.tsx b/frontend/src/modules/Map/settings/settings.tsx similarity index 97% rename from frontend/src/modules/Map/MapSettings.tsx rename to frontend/src/modules/Map/settings/settings.tsx index 3e1a2cd3e..3008e7e61 100644 --- a/frontend/src/modules/Map/MapSettings.tsx +++ b/frontend/src/modules/Map/settings/settings.tsx @@ -19,8 +19,12 @@ import { FullSurfaceAddress, SurfaceAddressBuilder, SurfaceDirectory, SurfaceTim import { useObservedSurfacesMetadataQuery, useRealizationSurfacesMetadataQuery } from "@modules/_shared/Surface"; import { usePropagateApiErrorToStatusWriter } from "@modules/_shared/hooks/usePropagateApiErrorToStatusWriter"; -import { MapState } from "./MapState"; -import { AggregationDropdown } from "./UiComponents"; +import { useSetAtom } from "jotai"; + +import { surfaceAddressAtom } from "./atoms/baseAtoms"; + +import { AggregationDropdown } from "../UiComponents"; +import { Interfaces } from "../interfaces"; const SurfaceTimeTypeEnumToStringMapping = { [SurfaceTimeType.None]: "Static", @@ -28,7 +32,7 @@ const SurfaceTimeTypeEnumToStringMapping = { [SurfaceTimeType.Interval]: "Time interval", }; //----------------------------------------------------------------------------------------------------------- -export function MapSettings(props: ModuleSettingsProps) { +export function MapSettings(props: ModuleSettingsProps) { const ensembleSet = useEnsembleSet(props.workbenchSession); const [selectedEnsembleIdent, setSelectedEnsembleIdent] = React.useState(null); const [timeType, setTimeType] = React.useState(SurfaceTimeType.None); @@ -41,6 +45,7 @@ export function MapSettings(props: ModuleSettingsProps) { const [selectedTimeOrInterval, setSelectedTimeOrInterval] = React.useState(null); const [aggregation, setAggregation] = React.useState(null); const [useObserved, toggleUseObserved] = React.useState(false); + const setSurfaceAddress = useSetAtom(surfaceAddressAtom); const syncedSettingKeys = props.settingsContext.useSyncedSettingKeys(); const syncHelper = new SyncSettingsHelper(syncedSettingKeys, props.workbenchServices); const syncedValueEnsembles = syncHelper.useValue(SyncSettingKey.ENSEMBLE, "global.syncValue.ensembles"); @@ -120,7 +125,7 @@ export function MapSettings(props: ModuleSettingsProps) { } console.debug(`propagateSurfaceSelectionToView() => ${surfaceAddress ? "valid surfAddr" : "NULL surfAddr"}`); - props.settingsContext.getStateStore().setValue("surfaceAddress", surfaceAddress); + setSurfaceAddress(surfaceAddress); }); function handleEnsembleSelectionChange(newEnsembleIdent: EnsembleIdent | null) { diff --git a/frontend/src/modules/Map/MapView.tsx b/frontend/src/modules/Map/view.tsx similarity index 93% rename from frontend/src/modules/Map/MapView.tsx rename to frontend/src/modules/Map/view.tsx index 4ed8b498b..e82842f58 100644 --- a/frontend/src/modules/Map/MapView.tsx +++ b/frontend/src/modules/Map/view.tsx @@ -9,11 +9,10 @@ import { usePropagateApiErrorToStatusWriter } from "@modules/_shared/hooks/usePr import { useSurfaceDataQueryByAddress } from "@modules_shared/Surface"; import SubsurfaceViewer from "@webviz/subsurface-viewer"; -import { MapState } from "./MapState"; +import { Interfaces } from "./interfaces"; -//----------------------------------------------------------------------------------------------------------- -export function MapView(props: ModuleViewProps) { - const surfaceAddress = props.viewContext.useStoreValue("surfaceAddress"); +export function MapView(props: ModuleViewProps): React.ReactNode { + const surfaceAddress = props.viewContext.useSettingsToViewInterfaceValue("surfaceAddress"); const statusWriter = useViewStatusWriter(props.viewContext); diff --git a/frontend/src/modules/MyModule/interfaces.ts b/frontend/src/modules/MyModule/interfaces.ts new file mode 100644 index 000000000..c3d406a60 --- /dev/null +++ b/frontend/src/modules/MyModule/interfaces.ts @@ -0,0 +1,34 @@ +import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; +import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; + +import { divMidPointAtom, gradientTypeAtom, maxAtom, minAtom, typeAtom } from "./settings/atoms/baseAtoms"; + +type SettingsToViewInterface = { + type: ColorScaleType; + gradientType: ColorScaleGradientType; + min: number; + max: number; + divMidPoint: number; +}; + +export type Interfaces = { + settingsToView: SettingsToViewInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { + type: (get) => { + return get(typeAtom); + }, + gradientType: (get) => { + return get(gradientTypeAtom); + }, + min: (get) => { + return get(minAtom); + }, + max: (get) => { + return get(maxAtom); + }, + divMidPoint: (get) => { + return get(divMidPointAtom); + }, +}; diff --git a/frontend/src/modules/MyModule/loadModule.tsx b/frontend/src/modules/MyModule/loadModule.tsx index 7e0a51ed1..6c34335d5 100644 --- a/frontend/src/modules/MyModule/loadModule.tsx +++ b/frontend/src/modules/MyModule/loadModule.tsx @@ -1,19 +1,10 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; -import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; -import { settings } from "./settings"; -import { State } from "./state"; +import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; +import { Settings } from "./settings/settings"; import { View } from "./view"; -const defaultState: State = { - type: ColorScaleType.Discrete, - gradientType: ColorScaleGradientType.Sequential, - min: 0, - max: 18, - divMidPoint: 9, -}; - -const module = ModuleRegistry.initModule("MyModule", defaultState); +const module = ModuleRegistry.initModule("MyModule", { settingsToViewInterfaceInitialization }); module.viewFC = View; -module.settingsFC = settings; +module.settingsFC = Settings; diff --git a/frontend/src/modules/MyModule/registerModule.ts b/frontend/src/modules/MyModule/registerModule.ts index 14c1cda66..77da7eb97 100644 --- a/frontend/src/modules/MyModule/registerModule.ts +++ b/frontend/src/modules/MyModule/registerModule.ts @@ -1,9 +1,9 @@ import { ModuleCategory, ModuleDevState } from "@framework/Module"; import { ModuleRegistry } from "@framework/ModuleRegistry"; -import { State } from "./state"; +import { Interfaces } from "./interfaces"; -ModuleRegistry.registerModule({ +ModuleRegistry.registerModule({ moduleName: "MyModule", defaultTitle: "My Module", category: ModuleCategory.DEBUG, diff --git a/frontend/src/modules/MyModule/settings/atoms/baseAtoms.ts b/frontend/src/modules/MyModule/settings/atoms/baseAtoms.ts new file mode 100644 index 000000000..59f519693 --- /dev/null +++ b/frontend/src/modules/MyModule/settings/atoms/baseAtoms.ts @@ -0,0 +1,9 @@ +import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; + +import { atom } from "jotai"; + +export const typeAtom = atom(ColorScaleType.Discrete); +export const gradientTypeAtom = atom(ColorScaleGradientType.Sequential); +export const minAtom = atom(0); +export const maxAtom = atom(18); +export const divMidPointAtom = atom(9); diff --git a/frontend/src/modules/MyModule/settings.tsx b/frontend/src/modules/MyModule/settings/settings.tsx similarity index 88% rename from frontend/src/modules/MyModule/settings.tsx rename to frontend/src/modules/MyModule/settings/settings.tsx index 4889a2b0e..e8adff2f2 100644 --- a/frontend/src/modules/MyModule/settings.tsx +++ b/frontend/src/modules/MyModule/settings/settings.tsx @@ -7,14 +7,18 @@ import { Label } from "@lib/components/Label"; import { RadioGroup } from "@lib/components/RadioGroup"; import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; -import { State } from "./state"; +import { useAtom } from "jotai"; -export const settings = (props: ModuleSettingsProps) => { - const [type, setType] = props.settingsContext.useStoreState("type"); - const [gradientType, setGradientType] = props.settingsContext.useStoreState("gradientType"); - const [min, setMin] = props.settingsContext.useStoreState("min"); - const [max, setMax] = props.settingsContext.useStoreState("max"); - const [divMidPoint, setDivMidPoint] = props.settingsContext.useStoreState("divMidPoint"); +import { divMidPointAtom, gradientTypeAtom, maxAtom, minAtom, typeAtom } from "./atoms/baseAtoms"; + +import { Interfaces } from "../interfaces"; + +export function Settings(props: ModuleSettingsProps): React.ReactNode { + const [type, setType] = useAtom(typeAtom); + const [gradientType, setGradientType] = useAtom(gradientTypeAtom); + const [min, setMin] = useAtom(minAtom); + const [max, setMax] = useAtom(maxAtom); + const [divMidPoint, setDivMidPoint] = useAtom(divMidPointAtom); function handleTypeChange(e: React.ChangeEvent) { setType(e.target.value as ColorScaleType); @@ -107,4 +111,4 @@ export const settings = (props: ModuleSettingsProps) => { )}
); -}; +} diff --git a/frontend/src/modules/MyModule/state.ts b/frontend/src/modules/MyModule/state.ts deleted file mode 100644 index 7681f121f..000000000 --- a/frontend/src/modules/MyModule/state.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; - -export type State = { - type: ColorScaleType; - gradientType: ColorScaleGradientType; - min: number; - max: number; - divMidPoint: number; -}; diff --git a/frontend/src/modules/MyModule/view.tsx b/frontend/src/modules/MyModule/view.tsx index e882845e5..f07a894df 100644 --- a/frontend/src/modules/MyModule/view.tsx +++ b/frontend/src/modules/MyModule/view.tsx @@ -7,7 +7,7 @@ import { ColorScaleType } from "@lib/utils/ColorScale"; import { PlotData } from "plotly.js"; -import { State } from "./state"; +import { Interfaces } from "./interfaces"; const countryData = [ "Belarus", @@ -402,12 +402,12 @@ for (let i = 0; i < countryData.length; i += 2) { alcConsumption.push(countryData[i + 1] as number); } -export const View = (props: ModuleViewProps) => { - const type = props.viewContext.useStoreValue("type"); - const gradientType = props.viewContext.useStoreValue("gradientType"); - const min = props.viewContext.useStoreValue("min"); - const max = props.viewContext.useStoreValue("max"); - const divMidPoint = props.viewContext.useStoreValue("divMidPoint"); +export function View(props: ModuleViewProps): React.ReactNode { + const type = props.viewContext.useSettingsToViewInterfaceValue("type"); + const gradientType = props.viewContext.useSettingsToViewInterfaceValue("gradientType"); + const min = props.viewContext.useSettingsToViewInterfaceValue("min"); + const max = props.viewContext.useSettingsToViewInterfaceValue("max"); + const divMidPoint = props.viewContext.useSettingsToViewInterfaceValue("divMidPoint"); const ref = React.useRef(null); @@ -444,4 +444,4 @@ export const View = (props: ModuleViewProps) => { ); -}; +} diff --git a/frontend/src/modules/MyModule2/interfaces.ts b/frontend/src/modules/MyModule2/interfaces.ts new file mode 100644 index 000000000..c74218dd6 --- /dev/null +++ b/frontend/src/modules/MyModule2/interfaces.ts @@ -0,0 +1,17 @@ +import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; + +import { textAtom } from "./atoms"; + +type SettingsToViewInterface = { + text: string; + derivedText: string; +}; + +export type Interfaces = { + settingsToView: SettingsToViewInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { + text: (get) => get(textAtom), + derivedText: (get) => get(textAtom).toUpperCase(), +}; diff --git a/frontend/src/modules/MyModule2/loadModule.tsx b/frontend/src/modules/MyModule2/loadModule.tsx index cccd7c747..d83e138bd 100644 --- a/frontend/src/modules/MyModule2/loadModule.tsx +++ b/frontend/src/modules/MyModule2/loadModule.tsx @@ -1,16 +1,11 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; import "./atoms"; +import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; import { Settings } from "./settings"; -import { SettingsToViewInterface, State, defaultState, interfaceDefinition } from "./state"; import { View } from "./view"; -const module = ModuleRegistry.initModule( - "MyModule2", - defaultState, - undefined, - interfaceDefinition -); +const module = ModuleRegistry.initModule("MyModule2", { settingsToViewInterfaceInitialization }); module.viewFC = View; module.settingsFC = Settings; diff --git a/frontend/src/modules/MyModule2/registerModule.ts b/frontend/src/modules/MyModule2/registerModule.ts index 1a992985d..0bfcc8169 100644 --- a/frontend/src/modules/MyModule2/registerModule.ts +++ b/frontend/src/modules/MyModule2/registerModule.ts @@ -1,9 +1,9 @@ import { ModuleCategory, ModuleDevState } from "@framework/Module"; import { ModuleRegistry } from "@framework/ModuleRegistry"; -import { SettingsToViewInterface, State } from "./state"; +import { Interfaces } from "./interfaces"; -ModuleRegistry.registerModule({ +ModuleRegistry.registerModule({ moduleName: "MyModule2", defaultTitle: "My Module 2", category: ModuleCategory.DEBUG, diff --git a/frontend/src/modules/MyModule2/settings.tsx b/frontend/src/modules/MyModule2/settings.tsx index dbd90340a..0528c3590 100644 --- a/frontend/src/modules/MyModule2/settings.tsx +++ b/frontend/src/modules/MyModule2/settings.tsx @@ -7,7 +7,7 @@ import { useAtom } from "jotai"; import { textAtom } from "./atoms"; -export const Settings = () => { +export function Settings(): React.ReactNode { const [atomText, setAtomText] = useAtom(textAtom); function handleAtomTextChange(event: React.ChangeEvent) { @@ -21,6 +21,6 @@ export const Settings = () => { ); -}; +} Settings.displayName = "Settings"; diff --git a/frontend/src/modules/MyModule2/state.ts b/frontend/src/modules/MyModule2/state.ts deleted file mode 100644 index eb9f9a6d3..000000000 --- a/frontend/src/modules/MyModule2/state.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; - -import { textAtom } from "./atoms"; - -export type State = { - text: string; -}; - -export const defaultState: State = { - text: "Hello World", -}; - -// ------------------------------------------------ - -export type SettingsToViewInterface = { - text: string; - derivedText: string; -}; - -export const interfaceDefinition: InterfaceInitialization = { - text: (get) => get(textAtom), - derivedText: (get) => get(textAtom).toUpperCase(), -}; diff --git a/frontend/src/modules/MyModule2/view.tsx b/frontend/src/modules/MyModule2/view.tsx index 5d095cdff..24145ea06 100644 --- a/frontend/src/modules/MyModule2/view.tsx +++ b/frontend/src/modules/MyModule2/view.tsx @@ -1,11 +1,9 @@ -import React from "react"; - import { ModuleViewProps } from "@framework/Module"; import { Label } from "@lib/components/Label"; -import { SettingsToViewInterface, State } from "./state"; +import { Interfaces } from "./interfaces"; -export const View = (props: ModuleViewProps) => { +export const View = (props: ModuleViewProps) => { const text = props.viewContext.useSettingsToViewInterfaceValue("text"); const derivedText = props.viewContext.useSettingsToViewInterfaceValue("derivedText"); diff --git a/frontend/src/modules/ParameterDistributionMatrix/settingsToViewInterface.ts b/frontend/src/modules/ParameterDistributionMatrix/interfaces.ts similarity index 85% rename from frontend/src/modules/ParameterDistributionMatrix/settingsToViewInterface.ts rename to frontend/src/modules/ParameterDistributionMatrix/interfaces.ts index 53e9478d9..f6d9b3f63 100644 --- a/frontend/src/modules/ParameterDistributionMatrix/settingsToViewInterface.ts +++ b/frontend/src/modules/ParameterDistributionMatrix/interfaces.ts @@ -10,7 +10,7 @@ import { import { selectedEnsembleIdentsAtom, selectedParameterIdentsAtom } from "./settings/atoms/derivedAtoms"; import { ParameterDistributionPlotType } from "./typesAndEnums"; -export type Interface = { +type SettingsToViewInterface = { selectedVisualizationType: ParameterDistributionPlotType; showIndividualRealizationValues: boolean; showPercentilesAndMeanLines: boolean; @@ -18,7 +18,11 @@ export type Interface = { selectedParameterIdents: ParameterIdent[]; }; -export const interfaceInitialization: InterfaceInitialization = { +export type Interfaces = { + settingsToView: SettingsToViewInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { selectedVisualizationType: (get) => { return get(selectedVisualizationTypeAtom); }, diff --git a/frontend/src/modules/ParameterDistributionMatrix/loadModule.tsx b/frontend/src/modules/ParameterDistributionMatrix/loadModule.tsx index 3375f4f97..ed2add361 100644 --- a/frontend/src/modules/ParameterDistributionMatrix/loadModule.tsx +++ b/frontend/src/modules/ParameterDistributionMatrix/loadModule.tsx @@ -1,14 +1,11 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; +import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; import { MODULE_NAME } from "./registerModule"; import { Settings } from "./settings/settings"; -import { Interface, interfaceInitialization } from "./settingsToViewInterface"; -import { State } from "./state"; import { View } from "./view/view"; -const defaultState: State = {}; - -const module = ModuleRegistry.initModule(MODULE_NAME, defaultState, {}, interfaceInitialization); +const module = ModuleRegistry.initModule(MODULE_NAME, { settingsToViewInterfaceInitialization }); module.viewFC = View; module.settingsFC = Settings; diff --git a/frontend/src/modules/ParameterDistributionMatrix/registerModule.tsx b/frontend/src/modules/ParameterDistributionMatrix/registerModule.tsx index a9d2496f6..eb6c1921c 100644 --- a/frontend/src/modules/ParameterDistributionMatrix/registerModule.tsx +++ b/frontend/src/modules/ParameterDistributionMatrix/registerModule.tsx @@ -1,14 +1,13 @@ import { ModuleCategory, ModuleDevState } from "@framework/Module"; import { ModuleRegistry } from "@framework/ModuleRegistry"; -import { Interface } from "./settingsToViewInterface"; -import { State } from "./state"; +import { Interfaces } from "./interfaces"; export const MODULE_NAME = "ParameterDistributionMatrix"; const description = "Plotting of parameter distributions"; -ModuleRegistry.registerModule({ +ModuleRegistry.registerModule({ moduleName: MODULE_NAME, defaultTitle: "Parameter Distribution Matrix", category: ModuleCategory.MAIN, diff --git a/frontend/src/modules/ParameterDistributionMatrix/settings/settings.tsx b/frontend/src/modules/ParameterDistributionMatrix/settings/settings.tsx index e42211a1c..417d4b8cf 100644 --- a/frontend/src/modules/ParameterDistributionMatrix/settings/settings.tsx +++ b/frontend/src/modules/ParameterDistributionMatrix/settings/settings.tsx @@ -24,12 +24,14 @@ import { selectedParameterIdentsAtom, } from "./atoms/derivedAtoms"; -import { Interface } from "../settingsToViewInterface"; -import { State } from "../state"; -import { MAX_PARAMETERS } from "../typesAndEnums"; -import { ParameterDistributionPlotType, ParameterDistributionPlotTypeEnumToStringMapping } from "../typesAndEnums"; +import { Interfaces } from "../interfaces"; +import { + MAX_PARAMETERS, + ParameterDistributionPlotType, + ParameterDistributionPlotTypeEnumToStringMapping, +} from "../typesAndEnums"; -export function Settings({ workbenchSession }: ModuleSettingsProps) { +export function Settings({ workbenchSession }: ModuleSettingsProps) { const ensembleSet = useEnsembleSet(workbenchSession); const selectedEnsembleIdents = useAtomValue(selectedEnsembleIdentsAtom); diff --git a/frontend/src/modules/ParameterDistributionMatrix/state.ts b/frontend/src/modules/ParameterDistributionMatrix/state.ts deleted file mode 100644 index 90ff7f709..000000000 --- a/frontend/src/modules/ParameterDistributionMatrix/state.ts +++ /dev/null @@ -1 +0,0 @@ -export type State = Record; diff --git a/frontend/src/modules/ParameterDistributionMatrix/view/view.tsx b/frontend/src/modules/ParameterDistributionMatrix/view/view.tsx index 3a69a9a6b..7b717a11d 100644 --- a/frontend/src/modules/ParameterDistributionMatrix/view/view.tsx +++ b/frontend/src/modules/ParameterDistributionMatrix/view/view.tsx @@ -9,11 +9,10 @@ import { useElementSize } from "@lib/hooks/useElementSize"; import { ParameterDistributionPlot } from "./components/ParameterDistributionPlot"; -import { Interface } from "../settingsToViewInterface"; -import { State } from "../state"; +import { Interfaces } from "../interfaces"; import { ParameterDataArr } from "../typesAndEnums"; -export function View(props: ModuleViewProps) { +export function View(props: ModuleViewProps) { const wrapperDivRef = React.useRef(null); const wrapperDivSize = useElementSize(wrapperDivRef); diff --git a/frontend/src/modules/Pvt/interfaces.ts b/frontend/src/modules/Pvt/interfaces.ts new file mode 100644 index 000000000..4e0020e56 --- /dev/null +++ b/frontend/src/modules/Pvt/interfaces.ts @@ -0,0 +1,41 @@ +import { EnsembleIdent } from "@framework/EnsembleIdent"; +import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; + +import { selectedColorByAtom, selectedDependentVariablesAtom, selectedPhaseAtom } from "./settings/atoms/baseAtoms"; +import { selectedEnsembleIdentsAtom, selectedPvtNumsAtom } from "./settings/atoms/derivedAtoms"; +import { pvtDataQueriesAtom } from "./settings/atoms/queryAtoms"; +import { ColorBy, CombinedPvtDataResult, PhaseType, PressureDependentVariable } from "./typesAndEnums"; + +type SettingsToViewInterface = { + selectedPhase: PhaseType; + selectedColorBy: ColorBy; + selectedDependentVariables: PressureDependentVariable[]; + selectedEnsembleIdents: EnsembleIdent[]; + selectedPvtNums: number[]; + pvtDataQueries: CombinedPvtDataResult; +}; + +export type Interfaces = { + settingsToView: SettingsToViewInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { + selectedPhase: (get) => { + return get(selectedPhaseAtom); + }, + selectedColorBy: (get) => { + return get(selectedColorByAtom); + }, + selectedDependentVariables: (get) => { + return get(selectedDependentVariablesAtom); + }, + selectedEnsembleIdents: (get) => { + return get(selectedEnsembleIdentsAtom); + }, + selectedPvtNums: (get) => { + return get(selectedPvtNumsAtom); + }, + pvtDataQueries: (get) => { + return get(pvtDataQueriesAtom); + }, +}; diff --git a/frontend/src/modules/Pvt/loadModule.tsx b/frontend/src/modules/Pvt/loadModule.tsx index 782c17c8d..28c1dbd3c 100644 --- a/frontend/src/modules/Pvt/loadModule.tsx +++ b/frontend/src/modules/Pvt/loadModule.tsx @@ -1,19 +1,11 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; +import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; import { MODULE_NAME } from "./registerModule"; import { Settings } from "./settings/settings"; -import { SettingsToViewInterface, interfaceInitialization } from "./settingsToViewInterface"; -import { State } from "./state"; import { View } from "./view"; -const defaultState: State = {}; - -const module = ModuleRegistry.initModule( - MODULE_NAME, - defaultState, - {}, - interfaceInitialization -); +const module = ModuleRegistry.initModule(MODULE_NAME, { settingsToViewInterfaceInitialization }); module.viewFC = View; module.settingsFC = Settings; diff --git a/frontend/src/modules/Pvt/registerModule.tsx b/frontend/src/modules/Pvt/registerModule.tsx index cc35d5f45..66895704a 100644 --- a/frontend/src/modules/Pvt/registerModule.tsx +++ b/frontend/src/modules/Pvt/registerModule.tsx @@ -2,16 +2,15 @@ import { ModuleCategory, ModuleDevState } from "@framework/Module"; import { ModuleDataTagId } from "@framework/ModuleDataTags"; import { ModuleRegistry } from "@framework/ModuleRegistry"; +import { Interfaces } from "./interfaces"; import { preview } from "./preview"; -import { SettingsToViewInterface } from "./settingsToViewInterface"; -import { State } from "./state"; export const MODULE_NAME = "Pvt"; const description = "Visualizes formation volume factor and viscosity data for oil, gas, and water from Eclipse init and include files."; -ModuleRegistry.registerModule({ +ModuleRegistry.registerModule({ moduleName: MODULE_NAME, defaultTitle: "PVT", category: ModuleCategory.MAIN, diff --git a/frontend/src/modules/Pvt/settings/settings.tsx b/frontend/src/modules/Pvt/settings/settings.tsx index 9bf651910..d36e9ffd4 100644 --- a/frontend/src/modules/Pvt/settings/settings.tsx +++ b/frontend/src/modules/Pvt/settings/settings.tsx @@ -29,8 +29,7 @@ import { import { pvtDataQueriesAtom } from "./atoms/queryAtoms"; import { DependentVariableSelector } from "./components/DependentVariableSelector/dependentVariableSelector"; -import { SettingsToViewInterface } from "../settingsToViewInterface"; -import { State } from "../state"; +import { Interfaces } from "../interfaces"; import { ColorBy, PHASE_TO_DISPLAY_NAME, @@ -40,7 +39,7 @@ import { } from "../typesAndEnums"; import { computeRealizationsIntersection } from "../utils/realizationsIntersection"; -export function Settings({ workbenchSession }: ModuleSettingsProps) { +export function Settings({ workbenchSession }: ModuleSettingsProps) { const ensembleSet = useEnsembleSet(workbenchSession); const filterEnsembleRealizationsFunc = useEnsembleRealizationFilterFunc(workbenchSession); diff --git a/frontend/src/modules/Pvt/state.ts b/frontend/src/modules/Pvt/state.ts deleted file mode 100644 index 90ff7f709..000000000 --- a/frontend/src/modules/Pvt/state.ts +++ /dev/null @@ -1 +0,0 @@ -export type State = Record; diff --git a/frontend/src/modules/Pvt/view.tsx b/frontend/src/modules/Pvt/view.tsx index 726c5c8cd..f76bc234c 100644 --- a/frontend/src/modules/Pvt/view.tsx +++ b/frontend/src/modules/Pvt/view.tsx @@ -11,16 +11,11 @@ import { useElementSize } from "@lib/hooks/useElementSize"; import { ContentMessage, ContentMessageType } from "@modules/_shared/components/ContentMessage/contentMessage"; import { makeDistinguishableEnsembleDisplayName } from "@modules/_shared/ensembleNameUtils"; -import { SettingsToViewInterface } from "./settingsToViewInterface"; -import { State } from "./state"; +import { Interfaces } from "./interfaces"; import { PvtDataAccessor } from "./utils/PvtDataAccessor"; import { PvtPlotBuilder } from "./utils/PvtPlotBuilder"; -export function View({ - viewContext, - workbenchSettings, - workbenchSession, -}: ModuleViewProps) { +export function View({ viewContext, workbenchSettings, workbenchSession }: ModuleViewProps) { const colorSet = workbenchSettings.useColorSet(); const statusWriter = useViewStatusWriter(viewContext); const ensembleSet = useEnsembleSet(workbenchSession); diff --git a/frontend/src/modules/Rft/interfaces.ts b/frontend/src/modules/Rft/interfaces.ts new file mode 100644 index 000000000..7f0c0e210 --- /dev/null +++ b/frontend/src/modules/Rft/interfaces.ts @@ -0,0 +1,16 @@ +import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; + +import { rftWellAddressAtom } from "./settings/atoms/baseAtoms"; +import { RftWellAddress } from "./typesAndEnums"; + +type SettingsToViewInterface = { rftWellAddress: RftWellAddress | null }; + +export type Interfaces = { + settingsToView: SettingsToViewInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { + rftWellAddress: (get) => { + return get(rftWellAddressAtom); + }, +}; diff --git a/frontend/src/modules/Rft/loadModule.tsx b/frontend/src/modules/Rft/loadModule.tsx index b4d79ae0d..3ee44a59f 100644 --- a/frontend/src/modules/Rft/loadModule.tsx +++ b/frontend/src/modules/Rft/loadModule.tsx @@ -1,14 +1,10 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; +import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; import { Settings } from "./settings"; -import State from "./state"; import { View } from "./view"; -const defaultState: State = { - rftWellAddress: null, -}; - -const module = ModuleRegistry.initModule("Rft", defaultState, {}); +const module = ModuleRegistry.initModule("Rft", { settingsToViewInterfaceInitialization }); module.viewFC = View; module.settingsFC = Settings; diff --git a/frontend/src/modules/Rft/registerModule.tsx b/frontend/src/modules/Rft/registerModule.tsx index 2c8a27e82..6fef6a40d 100644 --- a/frontend/src/modules/Rft/registerModule.tsx +++ b/frontend/src/modules/Rft/registerModule.tsx @@ -2,11 +2,11 @@ import { ModuleCategory, ModuleDevState } from "@framework/Module"; import { ModuleDataTagId } from "@framework/ModuleDataTags"; import { ModuleRegistry } from "@framework/ModuleRegistry"; -import State from "./state"; +import { Interfaces } from "./interfaces"; const description = "Plotting of simulated RFT results."; -ModuleRegistry.registerModule({ +ModuleRegistry.registerModule({ moduleName: "Rft", defaultTitle: "RFT", category: ModuleCategory.MAIN, diff --git a/frontend/src/modules/Rft/settings.tsx b/frontend/src/modules/Rft/settings.tsx index c3fc1a29c..03e520292 100644 --- a/frontend/src/modules/Rft/settings.tsx +++ b/frontend/src/modules/Rft/settings.tsx @@ -10,10 +10,13 @@ import { timestampUtcMsToCompactIsoString } from "@framework/utils/timestampUtil import { CollapsibleGroup } from "@lib/components/CollapsibleGroup"; import { Select, SelectOption } from "@lib/components/Select"; +import { useAtom } from "jotai"; import { isEqual } from "lodash"; +import { Interfaces } from "./interfaces"; import { useRftWellList } from "./queryHooks"; -import state, { RftWellAddress } from "./state"; +import { rftWellAddressAtom } from "./settings/atoms/baseAtoms"; +import { RftWellAddress } from "./typesAndEnums"; //Helpers to populate dropdowns const stringToOptions = (strings: string[]): SelectOption[] => { @@ -26,9 +29,9 @@ const timepointOptions = (timePoints: number[]): SelectOption[] => { })); }; -export function Settings({ settingsContext, workbenchServices, workbenchSession }: ModuleSettingsProps) { +export function Settings({ settingsContext, workbenchServices, workbenchSession }: ModuleSettingsProps) { const ensembleSet = useEnsembleSet(workbenchSession); - const [rftWellAddress, setRftWellAddress] = settingsContext.useStoreState("rftWellAddress"); + const [rftWellAddress, setRftWellAddress] = useAtom(rftWellAddressAtom); const [selectedEnsembleIdent, setSelectedEnsembleIdent] = React.useState(null); const [selectedWellName, setSelectedWellName] = React.useState(null); const [selectedTimePoint, setSelectedTimePoint] = React.useState(null); diff --git a/frontend/src/modules/Rft/settings/atoms/baseAtoms.ts b/frontend/src/modules/Rft/settings/atoms/baseAtoms.ts new file mode 100644 index 000000000..2d4fa5667 --- /dev/null +++ b/frontend/src/modules/Rft/settings/atoms/baseAtoms.ts @@ -0,0 +1,5 @@ +import { RftWellAddress } from "@modules/Rft/typesAndEnums"; + +import { atom } from "jotai"; + +export const rftWellAddressAtom = atom(null); diff --git a/frontend/src/modules/Rft/state.ts b/frontend/src/modules/Rft/typesAndEnums.ts similarity index 50% rename from frontend/src/modules/Rft/state.ts rename to frontend/src/modules/Rft/typesAndEnums.ts index a51b1c264..07c7959f6 100644 --- a/frontend/src/modules/Rft/state.ts +++ b/frontend/src/modules/Rft/typesAndEnums.ts @@ -1,11 +1,9 @@ -export interface RftWellAddress { +export type RftWellAddress = { addressType: "realizations"; caseUuid: string; ensembleName: string; wellName: string; responseName: string; timePoint: number; - realizationNums: number[] | null - -} -export default interface State { rftWellAddress: RftWellAddress | null } + realizationNums: number[] | null; +}; diff --git a/frontend/src/modules/Rft/view.tsx b/frontend/src/modules/Rft/view.tsx index 70c9ca3b2..bb9e56d48 100644 --- a/frontend/src/modules/Rft/view.tsx +++ b/frontend/src/modules/Rft/view.tsx @@ -8,13 +8,13 @@ import { useElementSize } from "@lib/hooks/useElementSize"; import { PlotData } from "plotly.js"; +import { Interfaces } from "./interfaces"; import { useRftRealizationData } from "./queryHooks"; -import State from "./state"; -export const View = ({ viewContext }: ModuleViewProps) => { +export const View = ({ viewContext }: ModuleViewProps) => { const wrapperDivRef = React.useRef(null); const wrapperDivSize = useElementSize(wrapperDivRef); - const rftWellAddress = viewContext.useStoreValue("rftWellAddress"); + const rftWellAddress = viewContext.useSettingsToViewInterfaceValue("rftWellAddress"); const rftRealizationDataQuery = useRftRealizationData( rftWellAddress?.caseUuid, rftWellAddress?.ensembleName, diff --git a/frontend/src/modules/SeismicIntersection/interfaces.ts b/frontend/src/modules/SeismicIntersection/interfaces.ts new file mode 100644 index 000000000..aee98b07c --- /dev/null +++ b/frontend/src/modules/SeismicIntersection/interfaces.ts @@ -0,0 +1,51 @@ +import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; +import { Wellbore } from "@framework/types/wellbore"; + +import { + extensionAtom, + seismicAddressAtom, + surfaceAddressAtom, + wellboreAddressAtom, + wellborePickCaseUuidAtom, + wellborePickSelectionAtom, + zScaleAtom, +} from "./settings/atoms/baseAtoms"; +import { SeismicAddress, SurfaceAddress, WellborePickSelectionType } from "./typesAndEnums"; + +type SettingsToViewInterface = { + wellboreAddress: Wellbore | null; + seismicAddress: SeismicAddress | null; + surfaceAddress: SurfaceAddress | null; + wellborePickCaseUuid: string | null; + wellborePickSelection: WellborePickSelectionType; + extension: number; + zScale: number; +}; + +export type Interfaces = { + settingsToView: SettingsToViewInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { + wellboreAddress: (get) => { + return get(wellboreAddressAtom); + }, + seismicAddress: (get) => { + return get(seismicAddressAtom); + }, + surfaceAddress: (get) => { + return get(surfaceAddressAtom); + }, + wellborePickCaseUuid: (get) => { + return get(wellborePickCaseUuidAtom); + }, + wellborePickSelection: (get) => { + return get(wellborePickSelectionAtom); + }, + extension: (get) => { + return get(extensionAtom); + }, + zScale: (get) => { + return get(zScaleAtom); + }, +}; diff --git a/frontend/src/modules/SeismicIntersection/loadModule.tsx b/frontend/src/modules/SeismicIntersection/loadModule.tsx index 91f0135ca..68b75e9d5 100644 --- a/frontend/src/modules/SeismicIntersection/loadModule.tsx +++ b/frontend/src/modules/SeismicIntersection/loadModule.tsx @@ -1,20 +1,10 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; -import { Settings } from "./settings"; -import { State, WellborePickSelectionType } from "./state"; +import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; +import { Settings } from "./settings/settings"; import { View } from "./view"; -const defaultState: State = { - wellboreAddress: null, - seismicAddress: null, - surfaceAddress: null, - wellborePickCaseUuid: null, - wellborePickSelection: WellborePickSelectionType.NONE, - extension: 1000, - zScale: 5, -}; - -const module = ModuleRegistry.initModule("SeismicIntersection", defaultState); +const module = ModuleRegistry.initModule("SeismicIntersection", { settingsToViewInterfaceInitialization }); module.viewFC = View; module.settingsFC = Settings; diff --git a/frontend/src/modules/SeismicIntersection/queryHooks.tsx b/frontend/src/modules/SeismicIntersection/queryHooks.tsx index cee1676d0..5ece3012c 100644 --- a/frontend/src/modules/SeismicIntersection/queryHooks.tsx +++ b/frontend/src/modules/SeismicIntersection/queryHooks.tsx @@ -1,7 +1,6 @@ import { Body_post_get_seismic_fence_api, Body_post_get_surface_intersection_api, - SeismicCubeMeta_api, SeismicFencePolyline_api, SurfaceIntersectionCumulativeLengthPolyline_api, SurfaceIntersectionData_api, @@ -14,19 +13,6 @@ import { SeismicFenceData_trans, transformSeismicFenceData } from "./utils/query const STALE_TIME = 60 * 1000; const CACHE_TIME = 60 * 1000; -export function useSeismicCubeMetaListQuery( - caseUuid: string | undefined, - ensembleName: string | undefined -): UseQueryResult { - return useQuery({ - queryKey: ["getSeismicCubeMetaList", caseUuid, ensembleName], - queryFn: () => apiService.seismic.getSeismicCubeMetaList(caseUuid ?? "", ensembleName ?? ""), - staleTime: STALE_TIME, - gcTime: CACHE_TIME, - enabled: !!(caseUuid && ensembleName), - }); -} - export function useSeismicFenceDataQuery( caseUuid: string | null, ensembleName: string | null, diff --git a/frontend/src/modules/SeismicIntersection/registerModule.ts b/frontend/src/modules/SeismicIntersection/registerModule.ts index bd55fbd20..ddf8b5f8b 100644 --- a/frontend/src/modules/SeismicIntersection/registerModule.ts +++ b/frontend/src/modules/SeismicIntersection/registerModule.ts @@ -3,11 +3,11 @@ import { ModuleDataTagId } from "@framework/ModuleDataTags"; import { ModuleRegistry } from "@framework/ModuleRegistry"; import { SyncSettingKey } from "@framework/SyncSettings"; -import { State } from "./state"; +import { Interfaces } from "./interfaces"; const description = "Visualization of intersection data with a wellbore and seismic fence."; -ModuleRegistry.registerModule({ +ModuleRegistry.registerModule({ moduleName: "SeismicIntersection", defaultTitle: "Seismic Intersection", category: ModuleCategory.MAIN, diff --git a/frontend/src/modules/SeismicIntersection/settings/atoms/baseAtoms.ts b/frontend/src/modules/SeismicIntersection/settings/atoms/baseAtoms.ts new file mode 100644 index 000000000..33d41c1ff --- /dev/null +++ b/frontend/src/modules/SeismicIntersection/settings/atoms/baseAtoms.ts @@ -0,0 +1,12 @@ +import { Wellbore } from "@framework/types/wellbore"; +import { SeismicAddress, SurfaceAddress, WellborePickSelectionType } from "@modules/SeismicIntersection/typesAndEnums"; + +import { atom } from "jotai"; + +export const wellboreAddressAtom = atom(null); +export const seismicAddressAtom = atom(null); +export const surfaceAddressAtom = atom(null); +export const wellborePickCaseUuidAtom = atom(null); +export const wellborePickSelectionAtom = atom(WellborePickSelectionType.NONE); +export const extensionAtom = atom(1000); +export const zScaleAtom = atom(5); diff --git a/frontend/src/modules/SeismicIntersection/settings/hooks/queryHooks.tsx b/frontend/src/modules/SeismicIntersection/settings/hooks/queryHooks.tsx new file mode 100644 index 000000000..1f16594a0 --- /dev/null +++ b/frontend/src/modules/SeismicIntersection/settings/hooks/queryHooks.tsx @@ -0,0 +1,19 @@ +import { SeismicCubeMeta_api } from "@api"; +import { apiService } from "@framework/ApiService"; +import { UseQueryResult, useQuery } from "@tanstack/react-query"; + +const STALE_TIME = 60 * 1000; +const CACHE_TIME = 60 * 1000; + +export function useSeismicCubeMetaListQuery( + caseUuid: string | undefined, + ensembleName: string | undefined +): UseQueryResult { + return useQuery({ + queryKey: ["getSeismicCubeMetaList", caseUuid, ensembleName], + queryFn: () => apiService.seismic.getSeismicCubeMetaList(caseUuid ?? "", ensembleName ?? ""), + staleTime: STALE_TIME, + gcTime: CACHE_TIME, + enabled: !!(caseUuid && ensembleName), + }); +} diff --git a/frontend/src/modules/SeismicIntersection/settings.tsx b/frontend/src/modules/SeismicIntersection/settings/settings.tsx similarity index 94% rename from frontend/src/modules/SeismicIntersection/settings.tsx rename to frontend/src/modules/SeismicIntersection/settings/settings.tsx index ca71cfc1c..52bfc0dce 100644 --- a/frontend/src/modules/SeismicIntersection/settings.tsx +++ b/frontend/src/modules/SeismicIntersection/settings/settings.tsx @@ -24,12 +24,28 @@ import { useRealizationSurfacesMetadataQuery } from "@modules/_shared/Surface"; import { useDrilledWellboreHeadersQuery } from "@modules/_shared/WellBore"; import { usePropagateApiErrorToStatusWriter } from "@modules/_shared/hooks/usePropagateApiErrorToStatusWriter"; +import { useAtom, useSetAtom } from "jotai"; import { isEqual } from "lodash"; -import { useSeismicCubeMetaListQuery } from "./queryHooks"; -import { State, WellborePickSelectionType, WellborePickSelectionTypeEnumToStringMapping } from "./state"; -import { SeismicAddress, SurfaceAddress } from "./types"; -import { SeismicCubeMetaDirectory, SeismicTimeType } from "./utils/seismicCubeDirectory"; +import { + extensionAtom, + seismicAddressAtom, + surfaceAddressAtom, + wellboreAddressAtom, + wellborePickCaseUuidAtom, + wellborePickSelectionAtom, + zScaleAtom, +} from "./atoms/baseAtoms"; +import { useSeismicCubeMetaListQuery } from "./hooks/queryHooks"; + +import { Interfaces } from "../interfaces"; +import { + SeismicAddress, + SurfaceAddress, + WellborePickSelectionType, + WellborePickSelectionTypeEnumToStringMapping, +} from "../typesAndEnums"; +import { SeismicCubeMetaDirectory, SeismicTimeType } from "../utils/seismicCubeDirectory"; const SeismicTimeTypeEnumToSurveyTypeStringMapping = { [SeismicTimeType.TimePoint]: "3D", @@ -60,20 +76,24 @@ const Z_SCALE_LIMITS = { min: 1, max: 100 }; // Minimum z-scale factor // Hardcoded surface time type - no surface as function of time const SURFACE_TIME_TYPE = SurfaceTimeType.None; -export function Settings({ settingsContext, workbenchSession, workbenchServices }: ModuleSettingsProps) { +export function Settings({ + settingsContext, + workbenchSession, + workbenchServices, +}: ModuleSettingsProps): React.ReactNode { const syncedSettingKeys = settingsContext.useSyncedSettingKeys(); const syncHelper = new SyncSettingsHelper(syncedSettingKeys, workbenchServices); const syncedValueEnsembles = syncHelper.useValue(SyncSettingKey.ENSEMBLE, "global.syncValue.ensembles"); const ensembleSet = useEnsembleSet(workbenchSession); const statusWriter = useSettingsStatusWriter(settingsContext); - const setSeismicAddress = settingsContext.useSetStoreValue("seismicAddress"); - const setSurfaceAddress = settingsContext.useSetStoreValue("surfaceAddress"); - const setWellboreAddress = settingsContext.useSetStoreValue("wellboreAddress"); - const setWellborePickCaseUuid = settingsContext.useSetStoreValue("wellborePickCaseUuid"); - const setWellborePickSelection = settingsContext.useSetStoreValue("wellborePickSelection"); - const [extension, setExtension] = settingsContext.useStoreState("extension"); - const [zScale, setZScale] = settingsContext.useStoreState("zScale"); + const setSeismicAddress = useSetAtom(seismicAddressAtom); + const setSurfaceAddress = useSetAtom(surfaceAddressAtom); + const [wellboreAddress, setWellboreAddress] = useAtom(wellboreAddressAtom); + const setWellborePickCaseUuid = useSetAtom(wellborePickCaseUuidAtom); + const [wellborePickSelection, setWellborePickSelection] = useAtom(wellborePickSelectionAtom); + const [extension, setExtension] = useAtom(extensionAtom); + const [zScale, setZScale] = useAtom(zScaleAtom); const [fetchedSurfaceNames, setFetchedSurfaceNames] = React.useState([]); const [fetchedSurfaceAttributes, setFetchedSurfaceAttributes] = React.useState([]); @@ -83,12 +103,9 @@ export function Settings({ settingsContext, workbenchSession, workbenchServices const [isObserved, setIsObserved] = React.useState(false); const [seismicTimeType, setSeismicTimeType] = React.useState(SeismicTimeType.TimePoint); - const [selectedWellboreAddress, setSelectedWellboreAddress] = React.useState( - settingsContext.useStoreValue("wellboreAddress") - ); - const [selectedWellborePickSelection, setSelectedWellborePickSelection] = React.useState( - settingsContext.useStoreValue("wellborePickSelection") - ); + const [selectedWellboreAddress, setSelectedWellboreAddress] = React.useState(wellboreAddress); + const [selectedWellborePickSelection, setSelectedWellborePickSelection] = + React.useState(wellborePickSelection); const candidateEnsembleIdent = maybeAssignFirstSyncedEnsemble(selectedEnsembleIdent, syncedValueEnsembles); const computedEnsembleIdent = fixupEnsembleIdent(candidateEnsembleIdent, ensembleSet); diff --git a/frontend/src/modules/SeismicIntersection/state.ts b/frontend/src/modules/SeismicIntersection/state.ts deleted file mode 100644 index 341b59ed3..000000000 --- a/frontend/src/modules/SeismicIntersection/state.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Wellbore } from "@framework/types/wellbore"; - -import { SeismicAddress, SurfaceAddress } from "./types"; - -export enum WellborePickSelectionType { - NONE = "None", - ALL = "All", - SELECTED_SURFACES = "SelectedSurfaces", -} - -export const WellborePickSelectionTypeEnumToStringMapping = { - [WellborePickSelectionType.NONE]: "None", - [WellborePickSelectionType.ALL]: "All", - [WellborePickSelectionType.SELECTED_SURFACES]: "Selected Surfaces", -}; - -export interface State { - wellboreAddress: Wellbore | null; - seismicAddress: SeismicAddress | null; - surfaceAddress: SurfaceAddress | null; - wellborePickCaseUuid: string | null; - wellborePickSelection: WellborePickSelectionType; - extension: number; - zScale: number; -} diff --git a/frontend/src/modules/SeismicIntersection/types.ts b/frontend/src/modules/SeismicIntersection/types.ts deleted file mode 100644 index ebcec448d..000000000 --- a/frontend/src/modules/SeismicIntersection/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -export type SeismicAddress = { - caseUuid: string; - ensemble: string; - realizationNumber: number; - attribute: string; - observed: boolean; - timeString?: string; -}; - -export type SurfaceAddress = { - caseUuid: string; - ensemble: string; - realizationNumber: number; - surfaceNames: string[]; - attribute: string; -}; diff --git a/frontend/src/modules/SeismicIntersection/typesAndEnums.ts b/frontend/src/modules/SeismicIntersection/typesAndEnums.ts new file mode 100644 index 000000000..b528075a6 --- /dev/null +++ b/frontend/src/modules/SeismicIntersection/typesAndEnums.ts @@ -0,0 +1,28 @@ +export type SeismicAddress = { + caseUuid: string; + ensemble: string; + realizationNumber: number; + attribute: string; + observed: boolean; + timeString?: string; +}; + +export type SurfaceAddress = { + caseUuid: string; + ensemble: string; + realizationNumber: number; + surfaceNames: string[]; + attribute: string; +}; + +export enum WellborePickSelectionType { + NONE = "None", + ALL = "All", + SELECTED_SURFACES = "SelectedSurfaces", +} + +export const WellborePickSelectionTypeEnumToStringMapping = { + [WellborePickSelectionType.NONE]: "None", + [WellborePickSelectionType.ALL]: "All", + [WellborePickSelectionType.SELECTED_SURFACES]: "Selected Surfaces", +}; diff --git a/frontend/src/modules/SeismicIntersection/view.tsx b/frontend/src/modules/SeismicIntersection/view.tsx index 2f0c7473b..f07dbf651 100644 --- a/frontend/src/modules/SeismicIntersection/view.tsx +++ b/frontend/src/modules/SeismicIntersection/view.tsx @@ -20,8 +20,9 @@ import { usePropagateApiErrorToStatusWriter } from "@modules/_shared/hooks/usePr import { isEqual } from "lodash"; +import { Interfaces } from "./interfaces"; import { useSeismicFenceDataQuery, useSurfaceIntersectionQueries } from "./queryHooks"; -import { State, WellborePickSelectionType } from "./state"; +import { WellborePickSelectionType } from "./typesAndEnums"; import { addMDOverlay, addSeismicLayer, @@ -44,7 +45,7 @@ import { useGenerateSeismicSliceImageData, } from "./utils/esvIntersectionHooks"; -export const View = ({ viewContext, workbenchSettings }: ModuleViewProps) => { +export function View({ viewContext, workbenchSettings }: ModuleViewProps): React.ReactNode { const wrapperDivRef = React.useRef(null); const wrapperDivSize = useElementSize(wrapperDivRef); const esvIntersectionContainerRef = React.useRef(null); @@ -52,13 +53,13 @@ export const View = ({ viewContext, workbenchSettings }: ModuleViewProps) const statusWriter = useViewStatusWriter(viewContext); - const seismicAddress = viewContext.useStoreValue("seismicAddress"); - const surfaceAddress = viewContext.useStoreValue("surfaceAddress"); - const wellboreAddress = viewContext.useStoreValue("wellboreAddress"); - const wellborePickCaseUuid = viewContext.useStoreValue("wellborePickCaseUuid"); - const wellborePickSelection = viewContext.useStoreValue("wellborePickSelection"); - const extension = viewContext.useStoreValue("extension"); - const zScale = viewContext.useStoreValue("zScale"); + const seismicAddress = viewContext.useSettingsToViewInterfaceValue("seismicAddress"); + const surfaceAddress = viewContext.useSettingsToViewInterfaceValue("surfaceAddress"); + const wellboreAddress = viewContext.useSettingsToViewInterfaceValue("wellboreAddress"); + const wellborePickCaseUuid = viewContext.useSettingsToViewInterfaceValue("wellborePickCaseUuid"); + const wellborePickSelection = viewContext.useSettingsToViewInterfaceValue("wellborePickSelection"); + const extension = viewContext.useSettingsToViewInterfaceValue("extension"); + const zScale = viewContext.useSettingsToViewInterfaceValue("zScale"); const seismicColorScale = workbenchSettings.useDiscreteColorScale({ gradientType: ColorScaleGradientType.Diverging, @@ -327,4 +328,4 @@ export const View = ({ viewContext, workbenchSettings }: ModuleViewProps) )} ); -}; +} diff --git a/frontend/src/modules/SimulationTimeSeries/settingsToViewInterface.ts b/frontend/src/modules/SimulationTimeSeries/interfaces.ts similarity index 91% rename from frontend/src/modules/SimulationTimeSeries/settingsToViewInterface.ts rename to frontend/src/modules/SimulationTimeSeries/interfaces.ts index b7fe3c36f..b1c2d1652 100644 --- a/frontend/src/modules/SimulationTimeSeries/settingsToViewInterface.ts +++ b/frontend/src/modules/SimulationTimeSeries/interfaces.ts @@ -28,7 +28,11 @@ export type SettingsToViewInterface = { resampleFrequency: Frequency_api | null; }; -export const interfaceInitialization: InterfaceInitialization = { +export type Interfaces = { + settingsToView: SettingsToViewInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { groupBy: (get) => { return get(groupByAtom); }, diff --git a/frontend/src/modules/SimulationTimeSeries/loadModule.tsx b/frontend/src/modules/SimulationTimeSeries/loadModule.tsx index 6697a50cd..914eb2dd8 100644 --- a/frontend/src/modules/SimulationTimeSeries/loadModule.tsx +++ b/frontend/src/modules/SimulationTimeSeries/loadModule.tsx @@ -1,21 +1,15 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; +import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; import { MODULE_NAME } from "./registerModule"; -import { SettingsAtoms, settingsAtomsInitialization } from "./settings/atoms/atomDefinitions"; import { Settings } from "./settings/settings"; -import { SettingsToViewInterface, interfaceInitialization } from "./settingsToViewInterface"; -import { State } from "./state"; -import { ViewAtoms, viewAtomsInitialization } from "./view/atoms/atomDefinitions"; +import { settingsToViewInterfaceEffects } from "./view/atoms/interfaceEffects"; import { View } from "./view/view"; -const module = ModuleRegistry.initModule( - MODULE_NAME, - {}, - undefined, - interfaceInitialization, - settingsAtomsInitialization, - viewAtomsInitialization -); +const module = ModuleRegistry.initModule(MODULE_NAME, { + settingsToViewInterfaceInitialization, + settingsToViewInterfaceEffects, +}); module.viewFC = View; module.settingsFC = Settings; diff --git a/frontend/src/modules/SimulationTimeSeries/registerModule.ts b/frontend/src/modules/SimulationTimeSeries/registerModule.ts index 2466ec8e1..679b53e4a 100644 --- a/frontend/src/modules/SimulationTimeSeries/registerModule.ts +++ b/frontend/src/modules/SimulationTimeSeries/registerModule.ts @@ -3,17 +3,14 @@ import { ModuleDataTagId } from "@framework/ModuleDataTags"; import { ModuleRegistry } from "@framework/ModuleRegistry"; import { channelDefs } from "./channelDefs"; +import { Interfaces } from "./interfaces"; import { preview } from "./preview"; -import { SettingsAtoms } from "./settings/atoms/atomDefinitions"; -import { SettingsToViewInterface } from "./settingsToViewInterface"; -import { State } from "./state"; -import { ViewAtoms } from "./view/atoms/atomDefinitions"; export const MODULE_NAME = "SimulationTimeSeries"; const description = "Plotting of simulation time series data."; -ModuleRegistry.registerModule({ +ModuleRegistry.registerModule({ moduleName: MODULE_NAME, defaultTitle: "Simulation Time Series", category: ModuleCategory.MAIN, diff --git a/frontend/src/modules/SimulationTimeSeries/settings/atoms/atomDefinitions.ts b/frontend/src/modules/SimulationTimeSeries/settings/atoms/atomDefinitions.ts deleted file mode 100644 index d699aec3a..000000000 --- a/frontend/src/modules/SimulationTimeSeries/settings/atoms/atomDefinitions.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ModuleAtoms } from "@framework/Module"; - -export type SettingsAtoms = Record; - -export function settingsAtomsInitialization(): ModuleAtoms { - return {}; -} diff --git a/frontend/src/modules/SimulationTimeSeries/settings/settings.tsx b/frontend/src/modules/SimulationTimeSeries/settings/settings.tsx index 9ce8be25b..c59e99824 100644 --- a/frontend/src/modules/SimulationTimeSeries/settings/settings.tsx +++ b/frontend/src/modules/SimulationTimeSeries/settings/settings.tsx @@ -48,7 +48,7 @@ import { import { vectorListQueriesAtom } from "./atoms/queryAtoms"; import { useMakeSettingsStatusWriterMessages } from "./hooks/useMakeSettingsStatusWriterMessages"; -import { SettingsToViewInterface } from "../settingsToViewInterface"; +import { Interfaces } from "../interfaces"; import { FanchartStatisticOption, FanchartStatisticOptionEnumToStringMapping, @@ -61,10 +61,7 @@ import { VisualizationModeEnumToStringMapping, } from "../typesAndEnums"; -export function Settings({ - settingsContext, - workbenchSession, -}: ModuleSettingsProps, SettingsToViewInterface>) { +export function Settings({ settingsContext, workbenchSession }: ModuleSettingsProps) { const ensembleSet = useEnsembleSet(workbenchSession); const statusWriter = useSettingsStatusWriter(settingsContext); diff --git a/frontend/src/modules/SimulationTimeSeries/state.ts b/frontend/src/modules/SimulationTimeSeries/state.ts deleted file mode 100644 index 90ff7f709..000000000 --- a/frontend/src/modules/SimulationTimeSeries/state.ts +++ /dev/null @@ -1 +0,0 @@ -export type State = Record; diff --git a/frontend/src/modules/SimulationTimeSeries/view/atoms/atomDefinitions.ts b/frontend/src/modules/SimulationTimeSeries/view/atoms/atomDefinitions.ts deleted file mode 100644 index 970af41ac..000000000 --- a/frontend/src/modules/SimulationTimeSeries/view/atoms/atomDefinitions.ts +++ /dev/null @@ -1,372 +0,0 @@ -import { - Frequency_api, - Observations_api, - VectorHistoricalData_api, - VectorRealizationData_api, - VectorStatisticData_api, -} from "@api"; -import { apiService } from "@framework/ApiService"; -import { EnsembleIdent } from "@framework/EnsembleIdent"; -import { EnsembleRealizationFilterFunctionAtom, EnsembleSetAtom } from "@framework/GlobalAtoms"; -import { ModuleAtoms } from "@framework/Module"; -import { UniDirectionalModuleComponentsInterface } from "@framework/UniDirectionalModuleComponentsInterface"; -import { atomWithQueries } from "@framework/utils/atomUtils"; -import { SettingsToViewInterface } from "@modules/SimulationTimeSeries/settingsToViewInterface"; -import { - EnsembleVectorObservationDataMap, - VectorSpec, - VisualizationMode, -} from "@modules/SimulationTimeSeries/typesAndEnums"; -import { QueryObserverResult } from "@tanstack/react-query"; - -import { atom } from "jotai"; - -import { createLoadedVectorSpecificationAndDataArray } from "../utils/vectorSpecificationsAndQueriesUtils"; - -export type ViewAtoms = { - userSelectedActiveTimestampUtcMs: number | null; - activeTimestampUtcMs: number | null; - vectorDataQueries: QueryObserverResult[]; - vectorStatisticsQueries: QueryObserverResult[]; - historicalVectorDataQueries: QueryObserverResult[]; - vectorObservationsQueries: { - isFetching: boolean; - isError: boolean; - ensembleVectorObservationDataMap: EnsembleVectorObservationDataMap; - }; - queryIsFetching: boolean; - realizationsQueryHasError: boolean; - statisticsQueryHasError: boolean; - historicalDataQueryHasError: boolean; - loadedVectorSpecificationsAndRealizationData: { - vectorSpecification: VectorSpec; - data: VectorRealizationData_api[]; - }[]; - loadedVectorSpecificationsAndStatisticsData: { - vectorSpecification: VectorSpec; - data: VectorStatisticData_api; - }[]; - loadedVectorSpecificationsAndHistoricalData: { - vectorSpecification: VectorSpec; - data: VectorHistoricalData_api; - }[]; - colorByParameter: boolean; -}; - -const STALE_TIME = 60 * 1000; -const CACHE_TIME = 60 * 1000; - -export function viewAtomsInitialization( - settingsToViewInterface: UniDirectionalModuleComponentsInterface -): ModuleAtoms { - const userSelectedActiveTimestampUtcMsAtom = atom(null); - - const validEnsembleRealizationsFunctionAtom = atom((get) => { - const ensembleSet = get(EnsembleSetAtom); - let validEnsembleRealizationsFunction = get(EnsembleRealizationFilterFunctionAtom); - - if (validEnsembleRealizationsFunction === null) { - validEnsembleRealizationsFunction = (ensembleIdent: EnsembleIdent) => { - return ensembleSet.findEnsemble(ensembleIdent)?.getRealizations() ?? []; - }; - } - - return validEnsembleRealizationsFunction; - }); - - const vectorDataQueriesAtom = atomWithQueries((get) => { - const vectorSpecifications = get(settingsToViewInterface.getAtom("vectorSpecifications")); - const resampleFrequency = get(settingsToViewInterface.getAtom("resampleFrequency")); - const visualizationMode = get(settingsToViewInterface.getAtom("visualizationMode")); - const validEnsembleRealizationsFunction = get(validEnsembleRealizationsFunctionAtom); - - const enabled = - visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS || - visualizationMode === VisualizationMode.STATISTICS_AND_REALIZATIONS; - - const queries = vectorSpecifications.map((item) => { - const realizations = [...validEnsembleRealizationsFunction(item.ensembleIdent)]; - return () => ({ - queryKey: [ - "getRealizationsVectorData", - item.ensembleIdent.getCaseUuid(), - item.ensembleIdent.getEnsembleName(), - item.vectorName, - resampleFrequency, - realizations, - ], - queryFn: () => - apiService.timeseries.getRealizationsVectorData( - item.ensembleIdent.getCaseUuid() ?? "", - item.ensembleIdent.getEnsembleName() ?? "", - item.vectorName ?? "", - resampleFrequency, - realizations - ), - staleTime: STALE_TIME, - gcTime: CACHE_TIME, - enabled: !!( - enabled && - item.vectorName && - item.ensembleIdent.getCaseUuid() && - item.ensembleIdent.getEnsembleName() - ), - }); - }); - - return { - queries, - }; - }); - - const vectorStatisticsQueriesAtom = atomWithQueries((get) => { - const vectorSpecifications = get(settingsToViewInterface.getAtom("vectorSpecifications")); - const resampleFrequency = get(settingsToViewInterface.getAtom("resampleFrequency")); - const visualizationMode = get(settingsToViewInterface.getAtom("visualizationMode")); - const validEnsembleRealizationsFunction = get(validEnsembleRealizationsFunctionAtom); - - const enabled = - visualizationMode === VisualizationMode.STATISTICAL_FANCHART || - visualizationMode === VisualizationMode.STATISTICAL_LINES || - visualizationMode === VisualizationMode.STATISTICS_AND_REALIZATIONS; - - const queries = vectorSpecifications.map((item) => { - const realizations = [...validEnsembleRealizationsFunction(item.ensembleIdent)]; - return () => ({ - queryKey: [ - "getStatisticalVectorData", - item.ensembleIdent.getCaseUuid(), - item.ensembleIdent.getEnsembleName(), - item.vectorName, - resampleFrequency, - realizations, - ], - queryFn: () => - apiService.timeseries.getStatisticalVectorData( - item.ensembleIdent.getCaseUuid() ?? "", - item.ensembleIdent.getEnsembleName() ?? "", - item.vectorName ?? "", - resampleFrequency ?? Frequency_api.MONTHLY, - undefined, - realizations - ), - staleTime: STALE_TIME, - gcTime: CACHE_TIME, - enabled: !!( - enabled && - item.vectorName && - item.ensembleIdent.getCaseUuid() && - item.ensembleIdent.getEnsembleName() - ), - }); - }); - - return { - queries, - }; - }); - - const historicalVectorDataQueriesAtom = atomWithQueries((get) => { - const vectorSpecifications = get(settingsToViewInterface.getAtom("vectorSpecifications")); - const resampleFrequency = get(settingsToViewInterface.getAtom("resampleFrequency")); - - const vectorSpecificationsWithHistoricalData = vectorSpecifications?.filter((vec) => vec.hasHistoricalVector); - const enabled = vectorSpecificationsWithHistoricalData?.some((vec) => vec.hasHistoricalVector) ?? false; - - const queries = vectorSpecifications.map((item) => { - return () => ({ - queryKey: [ - "getHistoricalVectorData", - item.ensembleIdent.getCaseUuid(), - item.ensembleIdent.getEnsembleName(), - item.vectorName, - resampleFrequency, - ], - queryFn: () => - apiService.timeseries.getHistoricalVectorData( - item.ensembleIdent.getCaseUuid() ?? "", - item.ensembleIdent.getEnsembleName() ?? "", - item.vectorName ?? "", - resampleFrequency ?? Frequency_api.MONTHLY - ), - staleTime: STALE_TIME, - gcTime: CACHE_TIME, - enabled: !!( - enabled && - item.vectorName && - item.ensembleIdent.getCaseUuid() && - item.ensembleIdent.getEnsembleName() - ), - }); - }); - - return { - queries, - }; - }); - - const vectorObservationsQueriesAtom = atomWithQueries((get) => { - const showObservations = get(settingsToViewInterface.getAtom("showObservations")); - const vectorSpecifications = get(settingsToViewInterface.getAtom("vectorSpecifications")); - - const uniqueEnsembleIdents = [...new Set(vectorSpecifications?.map((item) => item.ensembleIdent) ?? [])]; - - const queries = uniqueEnsembleIdents.map((item) => { - return () => ({ - queryKey: ["getObservations", item.getCaseUuid()], - queryFn: () => apiService.observations.getObservations(item.getCaseUuid() ?? ""), - staleTime: STALE_TIME, - gcTime: CACHE_TIME, - enabled: !!(showObservations && item.getCaseUuid()), - }); - }); - - return { - queries, - combine: (results: QueryObserverResult[]) => { - const combinedResult: EnsembleVectorObservationDataMap = new Map(); - if (!vectorSpecifications) - return { isFetching: false, isError: false, ensembleVectorObservationDataMap: combinedResult }; - - results.forEach((result, index) => { - const ensembleIdent = uniqueEnsembleIdents.at(index); - if (!ensembleIdent) return; - - const ensembleVectorSpecifications = vectorSpecifications.filter( - (item) => item.ensembleIdent === ensembleIdent - ); - - const ensembleHasObservations = result.data?.summary.length !== 0; - combinedResult.set(ensembleIdent, { - hasSummaryObservations: ensembleHasObservations, - vectorsObservationData: [], - }); - for (const vectorSpec of ensembleVectorSpecifications) { - const vectorObservationsData = - result.data?.summary.find((elm) => elm.vector_name === vectorSpec.vectorName) ?? null; - if (!vectorObservationsData) continue; - - combinedResult.get(ensembleIdent)?.vectorsObservationData.push({ - vectorSpecification: vectorSpec, - data: vectorObservationsData, - }); - } - }); - - return { - isFetching: results.some((result) => result.isFetching), - isError: results.some((result) => result.isError), - ensembleVectorObservationDataMap: combinedResult, - }; - }, - }; - }); - - const queryIsFetchingAtom = atom((get) => { - const vectorDataQueries = get(vectorDataQueriesAtom); - const vectorStatisticsQueries = get(vectorStatisticsQueriesAtom); - const historicalVectorDataQueries = get(historicalVectorDataQueriesAtom); - const vectorObservationsQueries = get(vectorObservationsQueriesAtom); - - const vectorDataIsFetching = vectorDataQueries.some((query) => query.isFetching); - const vectorStatisticsIsFetching = vectorStatisticsQueries.some((query) => query.isFetching); - const historicalVectorDataIsFetching = historicalVectorDataQueries.some((query) => query.isFetching); - const vectorObservationsIsFetching = vectorObservationsQueries.isFetching; - - const isFetching = - vectorDataIsFetching || - vectorStatisticsIsFetching || - historicalVectorDataIsFetching || - vectorObservationsIsFetching; - - return isFetching; - }); - - const realizationsQueryHasErrorAtom = atom((get) => { - const vectorDataQueries = get(vectorDataQueriesAtom); - - return vectorDataQueries.some((query) => query.isError); - }); - - const statisticsQueryHasErrorAtom = atom((get) => { - const vectorStatisticsQueries = get(vectorStatisticsQueriesAtom); - - return vectorStatisticsQueries.some((query) => query.isError); - }); - - const historicalDataQueryHasErrorAtom = atom((get) => { - const historicalVectorDataQueries = get(historicalVectorDataQueriesAtom); - - return historicalVectorDataQueries.some((query) => query.isError); - }); - - const loadedVectorSpecificationsAndRealizationDataAtom = atom((get) => { - const vectorDataQueries = get(vectorDataQueriesAtom); - const vectorSpecifications = get(settingsToViewInterface.getAtom("vectorSpecifications")); - - return createLoadedVectorSpecificationAndDataArray(vectorSpecifications, vectorDataQueries); - }); - - const loadedVectorSpecificationsAndStatisticsDataAtom = atom((get) => { - const vectorStatisticsQueries = get(vectorStatisticsQueriesAtom); - const vectorSpecifications = get(settingsToViewInterface.getAtom("vectorSpecifications")); - - return createLoadedVectorSpecificationAndDataArray(vectorSpecifications, vectorStatisticsQueries); - }); - - const loadedVectorSpecificationsAndHistoricalDataAtom = atom((get) => { - const historicalVectorDataQueries = get(historicalVectorDataQueriesAtom); - const vectorSpecifications = get(settingsToViewInterface.getAtom("vectorSpecifications")); - - return createLoadedVectorSpecificationAndDataArray(vectorSpecifications, historicalVectorDataQueries); - }); - - const activeTimestampUtcMsAtom = atom((get) => { - const loadedVectorSpecificationsAndRealizationData = get(loadedVectorSpecificationsAndRealizationDataAtom); - const isQueryFetching = get(queryIsFetchingAtom); - const userSelectedActiveTimestampUtcMs = get(userSelectedActiveTimestampUtcMsAtom); - - if ( - !isQueryFetching && - userSelectedActiveTimestampUtcMs === null && - loadedVectorSpecificationsAndRealizationData.length > 0 - ) { - const firstTimeStamp = - loadedVectorSpecificationsAndRealizationData.at(0)?.data.at(0)?.timestamps_utc_ms[0] ?? null; - return firstTimeStamp; - } - - return userSelectedActiveTimestampUtcMs; - }); - - const colorByParameterAtom = atom((get) => { - const colorRealizationsByParameter = get(settingsToViewInterface.getAtom("colorByParameter")); - const visualizationMode = get(settingsToViewInterface.getAtom("visualizationMode")); - const parameterIdent = get(settingsToViewInterface.getAtom("parameterIdent")); - const selectedEnsembles = get(settingsToViewInterface.getAtom("selectedEnsembles")); - - return ( - colorRealizationsByParameter && - visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS && - parameterIdent !== null && - selectedEnsembles.some((ensemble) => ensemble.getParameters().hasParameter(parameterIdent)) - ); - }); - - return { - userSelectedActiveTimestampUtcMs: userSelectedActiveTimestampUtcMsAtom, - activeTimestampUtcMs: activeTimestampUtcMsAtom, - vectorDataQueries: vectorDataQueriesAtom, - vectorStatisticsQueries: vectorStatisticsQueriesAtom, - historicalVectorDataQueries: historicalVectorDataQueriesAtom, - vectorObservationsQueries: vectorObservationsQueriesAtom, - queryIsFetching: queryIsFetchingAtom, - realizationsQueryHasError: realizationsQueryHasErrorAtom, - statisticsQueryHasError: statisticsQueryHasErrorAtom, - historicalDataQueryHasError: historicalDataQueryHasErrorAtom, - loadedVectorSpecificationsAndRealizationData: loadedVectorSpecificationsAndRealizationDataAtom, - loadedVectorSpecificationsAndStatisticsData: loadedVectorSpecificationsAndStatisticsDataAtom, - loadedVectorSpecificationsAndHistoricalData: loadedVectorSpecificationsAndHistoricalDataAtom, - colorByParameter: colorByParameterAtom, - }; -} diff --git a/frontend/src/modules/SimulationTimeSeries/view/atoms/baseAtoms.ts b/frontend/src/modules/SimulationTimeSeries/view/atoms/baseAtoms.ts new file mode 100644 index 000000000..8c0d836d5 --- /dev/null +++ b/frontend/src/modules/SimulationTimeSeries/view/atoms/baseAtoms.ts @@ -0,0 +1,15 @@ +import { Frequency_api } from "@api"; +import { Ensemble } from "@framework/Ensemble"; +import { ParameterIdent } from "@framework/EnsembleParameters"; +import { VectorSpec, VisualizationMode } from "@modules/SimulationTimeSeries/typesAndEnums"; + +import { atom } from "jotai"; + +export const userSelectedActiveTimestampUtcMsAtom = atom(null); +export const vectorSpecificationsAtom = atom([]); +export const resampleFrequencyAtom = atom(null); +export const visualizationModeAtom = atom(VisualizationMode.STATISTICAL_FANCHART); +export const showObservationsAtom = atom(true); +export const interfaceColorByParameterAtom = atom(false); +export const parameterIdentAtom = atom(null); +export const selectedEnsemblesAtom = atom([]); diff --git a/frontend/src/modules/SimulationTimeSeries/view/atoms/derivedAtoms.ts b/frontend/src/modules/SimulationTimeSeries/view/atoms/derivedAtoms.ts new file mode 100644 index 000000000..6290fd739 --- /dev/null +++ b/frontend/src/modules/SimulationTimeSeries/view/atoms/derivedAtoms.ts @@ -0,0 +1,126 @@ +import { EnsembleIdent } from "@framework/EnsembleIdent"; +import { EnsembleRealizationFilterFunctionAtom, EnsembleSetAtom } from "@framework/GlobalAtoms"; +import { VisualizationMode } from "@modules/SimulationTimeSeries/typesAndEnums"; + +import { atom } from "jotai"; + +import { + interfaceColorByParameterAtom, + parameterIdentAtom, + selectedEnsemblesAtom, + userSelectedActiveTimestampUtcMsAtom, + vectorSpecificationsAtom, + visualizationModeAtom, +} from "./baseAtoms"; +import { + historicalVectorDataQueriesAtom, + vectorDataQueriesAtom, + vectorObservationsQueriesAtom, + vectorStatisticsQueriesAtom, +} from "./queryAtoms"; + +import { createLoadedVectorSpecificationAndDataArray } from "../utils/vectorSpecificationsAndQueriesUtils"; + +export const validEnsembleRealizationsFunctionAtom = atom((get) => { + const ensembleSet = get(EnsembleSetAtom); + let validEnsembleRealizationsFunction = get(EnsembleRealizationFilterFunctionAtom); + + if (validEnsembleRealizationsFunction === null) { + validEnsembleRealizationsFunction = (ensembleIdent: EnsembleIdent) => { + return ensembleSet.findEnsemble(ensembleIdent)?.getRealizations() ?? []; + }; + } + + return validEnsembleRealizationsFunction; +}); + +export const queryIsFetchingAtom = atom((get) => { + const vectorDataQueries = get(vectorDataQueriesAtom); + const vectorStatisticsQueries = get(vectorStatisticsQueriesAtom); + const historicalVectorDataQueries = get(historicalVectorDataQueriesAtom); + const vectorObservationsQueries = get(vectorObservationsQueriesAtom); + + const vectorDataIsFetching = vectorDataQueries.some((query) => query.isFetching); + const vectorStatisticsIsFetching = vectorStatisticsQueries.some((query) => query.isFetching); + const historicalVectorDataIsFetching = historicalVectorDataQueries.some((query) => query.isFetching); + const vectorObservationsIsFetching = vectorObservationsQueries.isFetching; + + const isFetching = + vectorDataIsFetching || + vectorStatisticsIsFetching || + historicalVectorDataIsFetching || + vectorObservationsIsFetching; + + return isFetching; +}); + +export const realizationsQueryHasErrorAtom = atom((get) => { + const vectorDataQueries = get(vectorDataQueriesAtom); + + return vectorDataQueries.some((query) => query.isError); +}); + +export const statisticsQueryHasErrorAtom = atom((get) => { + const vectorStatisticsQueries = get(vectorStatisticsQueriesAtom); + + return vectorStatisticsQueries.some((query) => query.isError); +}); + +export const historicalDataQueryHasErrorAtom = atom((get) => { + const historicalVectorDataQueries = get(historicalVectorDataQueriesAtom); + + return historicalVectorDataQueries.some((query) => query.isError); +}); + +export const loadedVectorSpecificationsAndRealizationDataAtom = atom((get) => { + const vectorDataQueries = get(vectorDataQueriesAtom); + const vectorSpecifications = get(vectorSpecificationsAtom); + + return createLoadedVectorSpecificationAndDataArray(vectorSpecifications, vectorDataQueries); +}); + +export const loadedVectorSpecificationsAndStatisticsDataAtom = atom((get) => { + const vectorStatisticsQueries = get(vectorStatisticsQueriesAtom); + const vectorSpecifications = get(vectorSpecificationsAtom); + + return createLoadedVectorSpecificationAndDataArray(vectorSpecifications, vectorStatisticsQueries); +}); + +export const loadedVectorSpecificationsAndHistoricalDataAtom = atom((get) => { + const historicalVectorDataQueries = get(historicalVectorDataQueriesAtom); + const vectorSpecifications = get(vectorSpecificationsAtom); + + return createLoadedVectorSpecificationAndDataArray(vectorSpecifications, historicalVectorDataQueries); +}); + +export const activeTimestampUtcMsAtom = atom((get) => { + const loadedVectorSpecificationsAndRealizationData = get(loadedVectorSpecificationsAndRealizationDataAtom); + const isQueryFetching = get(queryIsFetchingAtom); + const userSelectedActiveTimestampUtcMs = get(userSelectedActiveTimestampUtcMsAtom); + + if ( + !isQueryFetching && + userSelectedActiveTimestampUtcMs === null && + loadedVectorSpecificationsAndRealizationData.length > 0 + ) { + const firstTimeStamp = + loadedVectorSpecificationsAndRealizationData.at(0)?.data.at(0)?.timestamps_utc_ms[0] ?? null; + return firstTimeStamp; + } + + return userSelectedActiveTimestampUtcMs; +}); + +export const colorByParameterAtom = atom((get) => { + const colorRealizationsByParameter = get(interfaceColorByParameterAtom); + const visualizationMode = get(visualizationModeAtom); + const parameterIdent = get(parameterIdentAtom); + const selectedEnsembles = get(selectedEnsemblesAtom); + + return ( + colorRealizationsByParameter && + visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS && + parameterIdent !== null && + selectedEnsembles.some((ensemble) => ensemble.getParameters().hasParameter(parameterIdent)) + ); +}); diff --git a/frontend/src/modules/SimulationTimeSeries/view/atoms/interfaceEffects.ts b/frontend/src/modules/SimulationTimeSeries/view/atoms/interfaceEffects.ts new file mode 100644 index 000000000..da265b642 --- /dev/null +++ b/frontend/src/modules/SimulationTimeSeries/view/atoms/interfaceEffects.ts @@ -0,0 +1,43 @@ +import { InterfaceEffects } from "@framework/Module"; +import { SettingsToViewInterface } from "@modules/SimulationTimeSeries/interfaces"; + +import { + interfaceColorByParameterAtom, + parameterIdentAtom, + resampleFrequencyAtom, + selectedEnsemblesAtom, + showObservationsAtom, + vectorSpecificationsAtom, + visualizationModeAtom, +} from "./baseAtoms"; + +export const settingsToViewInterfaceEffects: InterfaceEffects = [ + (getInterfaceValue, setAtomValue) => { + const vectorSpecifications = getInterfaceValue("vectorSpecifications"); + setAtomValue(vectorSpecificationsAtom, vectorSpecifications); + }, + (getInterfaceValue, setAtomValue) => { + const resampleFrequency = getInterfaceValue("resampleFrequency"); + setAtomValue(resampleFrequencyAtom, resampleFrequency); + }, + (getInterfaceValue, setAtomValue) => { + const visualizationMode = getInterfaceValue("visualizationMode"); + setAtomValue(visualizationModeAtom, visualizationMode); + }, + (getInterfaceValue, setAtomValue) => { + const showObservations = getInterfaceValue("showObservations"); + setAtomValue(showObservationsAtom, showObservations); + }, + (getInterfaceValue, setAtomValue) => { + const interfaceColorByParameter = getInterfaceValue("colorByParameter"); + setAtomValue(interfaceColorByParameterAtom, interfaceColorByParameter); + }, + (getInterfaceValue, setAtomValue) => { + const parameterIdent = getInterfaceValue("parameterIdent"); + setAtomValue(parameterIdentAtom, parameterIdent); + }, + (getInterfaceValue, setAtomValue) => { + const selectedEnsembles = getInterfaceValue("selectedEnsembles"); + setAtomValue(selectedEnsemblesAtom, selectedEnsembles); + }, +]; diff --git a/frontend/src/modules/SimulationTimeSeries/view/atoms/queryAtoms.ts b/frontend/src/modules/SimulationTimeSeries/view/atoms/queryAtoms.ts new file mode 100644 index 000000000..13b4ebe29 --- /dev/null +++ b/frontend/src/modules/SimulationTimeSeries/view/atoms/queryAtoms.ts @@ -0,0 +1,204 @@ +import { Frequency_api, Observations_api } from "@api"; +import { apiService } from "@framework/ApiService"; +import { atomWithQueries } from "@framework/utils/atomUtils"; +import { EnsembleVectorObservationDataMap, VisualizationMode } from "@modules/SimulationTimeSeries/typesAndEnums"; +import { QueryObserverResult } from "@tanstack/react-query"; + +import { + resampleFrequencyAtom, + showObservationsAtom, + vectorSpecificationsAtom, + visualizationModeAtom, +} from "./baseAtoms"; +import { validEnsembleRealizationsFunctionAtom } from "./derivedAtoms"; + +const STALE_TIME = 60 * 1000; +const CACHE_TIME = 60 * 1000; + +export const vectorDataQueriesAtom = atomWithQueries((get) => { + const vectorSpecifications = get(vectorSpecificationsAtom); + const resampleFrequency = get(resampleFrequencyAtom); + const visualizationMode = get(visualizationModeAtom); + const validEnsembleRealizationsFunction = get(validEnsembleRealizationsFunctionAtom); + + const enabled = + visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS || + visualizationMode === VisualizationMode.STATISTICS_AND_REALIZATIONS; + + const queries = vectorSpecifications.map((item) => { + const realizations = [...validEnsembleRealizationsFunction(item.ensembleIdent)]; + return () => ({ + queryKey: [ + "getRealizationsVectorData", + item.ensembleIdent.getCaseUuid(), + item.ensembleIdent.getEnsembleName(), + item.vectorName, + resampleFrequency, + realizations, + ], + queryFn: () => + apiService.timeseries.getRealizationsVectorData( + item.ensembleIdent.getCaseUuid() ?? "", + item.ensembleIdent.getEnsembleName() ?? "", + item.vectorName ?? "", + resampleFrequency, + realizations + ), + staleTime: STALE_TIME, + gcTime: CACHE_TIME, + enabled: !!( + enabled && + item.vectorName && + item.ensembleIdent.getCaseUuid() && + item.ensembleIdent.getEnsembleName() + ), + }); + }); + + return { + queries, + }; +}); + +export const vectorStatisticsQueriesAtom = atomWithQueries((get) => { + const vectorSpecifications = get(vectorSpecificationsAtom); + const resampleFrequency = get(resampleFrequencyAtom); + const visualizationMode = get(visualizationModeAtom); + const validEnsembleRealizationsFunction = get(validEnsembleRealizationsFunctionAtom); + + const enabled = + visualizationMode === VisualizationMode.STATISTICAL_FANCHART || + visualizationMode === VisualizationMode.STATISTICAL_LINES || + visualizationMode === VisualizationMode.STATISTICS_AND_REALIZATIONS; + + const queries = vectorSpecifications.map((item) => { + const realizations = [...validEnsembleRealizationsFunction(item.ensembleIdent)]; + return () => ({ + queryKey: [ + "getStatisticalVectorData", + item.ensembleIdent.getCaseUuid(), + item.ensembleIdent.getEnsembleName(), + item.vectorName, + resampleFrequency, + realizations, + ], + queryFn: () => + apiService.timeseries.getStatisticalVectorData( + item.ensembleIdent.getCaseUuid() ?? "", + item.ensembleIdent.getEnsembleName() ?? "", + item.vectorName ?? "", + resampleFrequency ?? Frequency_api.MONTHLY, + undefined, + realizations + ), + staleTime: STALE_TIME, + gcTime: CACHE_TIME, + enabled: !!( + enabled && + item.vectorName && + item.ensembleIdent.getCaseUuid() && + item.ensembleIdent.getEnsembleName() + ), + }); + }); + + return { + queries, + }; +}); + +export const historicalVectorDataQueriesAtom = atomWithQueries((get) => { + const vectorSpecifications = get(vectorSpecificationsAtom); + const resampleFrequency = get(resampleFrequencyAtom); + + const vectorSpecificationsWithHistoricalData = vectorSpecifications?.filter((vec) => vec.hasHistoricalVector); + const enabled = vectorSpecificationsWithHistoricalData?.some((vec) => vec.hasHistoricalVector) ?? false; + + const queries = vectorSpecifications.map((item) => { + return () => ({ + queryKey: [ + "getHistoricalVectorData", + item.ensembleIdent.getCaseUuid(), + item.ensembleIdent.getEnsembleName(), + item.vectorName, + resampleFrequency, + ], + queryFn: () => + apiService.timeseries.getHistoricalVectorData( + item.ensembleIdent.getCaseUuid() ?? "", + item.ensembleIdent.getEnsembleName() ?? "", + item.vectorName ?? "", + resampleFrequency ?? Frequency_api.MONTHLY + ), + staleTime: STALE_TIME, + gcTime: CACHE_TIME, + enabled: !!( + enabled && + item.vectorName && + item.ensembleIdent.getCaseUuid() && + item.ensembleIdent.getEnsembleName() + ), + }); + }); + + return { + queries, + }; +}); + +export const vectorObservationsQueriesAtom = atomWithQueries((get) => { + const showObservations = get(showObservationsAtom); + const vectorSpecifications = get(vectorSpecificationsAtom); + + const uniqueEnsembleIdents = [...new Set(vectorSpecifications?.map((item) => item.ensembleIdent) ?? [])]; + + const queries = uniqueEnsembleIdents.map((item) => { + return () => ({ + queryKey: ["getObservations", item.getCaseUuid()], + queryFn: () => apiService.observations.getObservations(item.getCaseUuid() ?? ""), + staleTime: STALE_TIME, + gcTime: CACHE_TIME, + enabled: !!(showObservations && item.getCaseUuid()), + }); + }); + + return { + queries, + combine: (results: QueryObserverResult[]) => { + const combinedResult: EnsembleVectorObservationDataMap = new Map(); + if (!vectorSpecifications) + return { isFetching: false, isError: false, ensembleVectorObservationDataMap: combinedResult }; + + results.forEach((result, index) => { + const ensembleIdent = uniqueEnsembleIdents.at(index); + if (!ensembleIdent) return; + + const ensembleVectorSpecifications = vectorSpecifications.filter( + (item) => item.ensembleIdent === ensembleIdent + ); + + const ensembleHasObservations = result.data?.summary.length !== 0; + combinedResult.set(ensembleIdent, { + hasSummaryObservations: ensembleHasObservations, + vectorsObservationData: [], + }); + for (const vectorSpec of ensembleVectorSpecifications) { + const vectorObservationsData = + result.data?.summary.find((elm) => elm.vector_name === vectorSpec.vectorName) ?? null; + if (!vectorObservationsData) continue; + + combinedResult.get(ensembleIdent)?.vectorsObservationData.push({ + vectorSpecification: vectorSpec, + data: vectorObservationsData, + }); + } + }); + + return { + isFetching: results.some((result) => result.isFetching), + isError: results.some((result) => result.isError), + ensembleVectorObservationDataMap: combinedResult, + }; + }, + }; +}); diff --git a/frontend/src/modules/SimulationTimeSeries/view/hooks/useMakeEnsembleDisplayNameFunc.ts b/frontend/src/modules/SimulationTimeSeries/view/hooks/useMakeEnsembleDisplayNameFunc.ts index 7977bea07..7ea14fab8 100644 --- a/frontend/src/modules/SimulationTimeSeries/view/hooks/useMakeEnsembleDisplayNameFunc.ts +++ b/frontend/src/modules/SimulationTimeSeries/view/hooks/useMakeEnsembleDisplayNameFunc.ts @@ -1,14 +1,10 @@ import { EnsembleIdent } from "@framework/EnsembleIdent"; import { ViewContext } from "@framework/ModuleContext"; -import { SettingsAtoms } from "@modules/SimulationTimeSeries/settings/atoms/atomDefinitions"; -import { SettingsToViewInterface } from "@modules/SimulationTimeSeries/settingsToViewInterface"; -import { State } from "@modules/SimulationTimeSeries/state"; +import { Interfaces } from "@modules/SimulationTimeSeries/interfaces"; import { makeDistinguishableEnsembleDisplayName } from "@modules/_shared/ensembleNameUtils"; -import { ViewAtoms } from "../atoms/atomDefinitions"; - export function useMakeEnsembleDisplayNameFunc( - viewContext: ViewContext + viewContext: ViewContext ): (ensembleIdent: EnsembleIdent) => string { const selectedEnsembles = viewContext.useSettingsToViewInterfaceValue("selectedEnsembles"); diff --git a/frontend/src/modules/SimulationTimeSeries/view/hooks/useMakeViewStatusWriterMessages.ts b/frontend/src/modules/SimulationTimeSeries/view/hooks/useMakeViewStatusWriterMessages.ts index 053017b7d..a00568236 100644 --- a/frontend/src/modules/SimulationTimeSeries/view/hooks/useMakeViewStatusWriterMessages.ts +++ b/frontend/src/modules/SimulationTimeSeries/view/hooks/useMakeViewStatusWriterMessages.ts @@ -2,27 +2,31 @@ import { Ensemble } from "@framework/Ensemble"; import { EnsembleSetAtom } from "@framework/GlobalAtoms"; import { ViewContext } from "@framework/ModuleContext"; import { ViewStatusWriter } from "@framework/StatusWriter"; -import { SettingsAtoms } from "@modules/SimulationTimeSeries/settings/atoms/atomDefinitions"; -import { SettingsToViewInterface } from "@modules/SimulationTimeSeries/settingsToViewInterface"; -import { State } from "@modules/SimulationTimeSeries/state"; +import { Interfaces } from "@modules/SimulationTimeSeries/interfaces"; import { useAtomValue } from "jotai"; -import { ViewAtoms } from "../atoms/atomDefinitions"; +import { + historicalDataQueryHasErrorAtom, + queryIsFetchingAtom, + realizationsQueryHasErrorAtom, + statisticsQueryHasErrorAtom, +} from "../atoms/derivedAtoms"; +import { vectorObservationsQueriesAtom } from "../atoms/queryAtoms"; export function useMakeViewStatusWriterMessages( - viewContext: ViewContext, + viewContext: ViewContext, statusWriter: ViewStatusWriter, parameterDisplayName: string | null, ensemblesWithoutParameter: Ensemble[] ) { const ensembleSet = useAtomValue(EnsembleSetAtom); const showObservations = viewContext.useSettingsToViewInterfaceValue("showObservations"); - const vectorObservationsQueries = viewContext.useViewAtomValue("vectorObservationsQueries"); - const isQueryFetching = viewContext.useViewAtomValue("queryIsFetching"); - const hasHistoricalVectorQueryError = viewContext.useViewAtomValue("historicalDataQueryHasError"); - const hasRealizationsQueryError = viewContext.useViewAtomValue("realizationsQueryHasError"); - const hasStatisticsQueryError = viewContext.useViewAtomValue("statisticsQueryHasError"); + const vectorObservationsQueries = useAtomValue(vectorObservationsQueriesAtom); + const isQueryFetching = useAtomValue(queryIsFetchingAtom); + const hasHistoricalVectorQueryError = useAtomValue(historicalDataQueryHasErrorAtom); + const hasRealizationsQueryError = useAtomValue(realizationsQueryHasErrorAtom); + const hasStatisticsQueryError = useAtomValue(statisticsQueryHasErrorAtom); statusWriter.setLoading(isQueryFetching); if (hasRealizationsQueryError) { diff --git a/frontend/src/modules/SimulationTimeSeries/view/hooks/usePublishToDataChannels.ts b/frontend/src/modules/SimulationTimeSeries/view/hooks/usePublishToDataChannels.ts index aad13300e..122bf4b74 100644 --- a/frontend/src/modules/SimulationTimeSeries/view/hooks/usePublishToDataChannels.ts +++ b/frontend/src/modules/SimulationTimeSeries/view/hooks/usePublishToDataChannels.ts @@ -1,23 +1,23 @@ import { ChannelContentDefinition } from "@framework/DataChannelTypes"; import { ViewContext } from "@framework/ModuleContext"; -import { SettingsAtoms } from "@modules/SimulationTimeSeries/settings/atoms/atomDefinitions"; -import { SettingsToViewInterface } from "@modules/SimulationTimeSeries/settingsToViewInterface"; -import { State } from "@modules/SimulationTimeSeries/state"; +import { Interfaces } from "@modules/SimulationTimeSeries/interfaces"; + +import { useAtomValue } from "jotai"; import { useMakeEnsembleDisplayNameFunc } from "./useMakeEnsembleDisplayNameFunc"; import { ChannelIds } from "../../channelDefs"; import { makeVectorGroupDataGenerator } from "../../dataGenerators"; -import { ViewAtoms } from "../atoms/atomDefinitions"; +import { + activeTimestampUtcMsAtom, + loadedVectorSpecificationsAndRealizationDataAtom, + queryIsFetchingAtom, +} from "../atoms/derivedAtoms"; -export function usePublishToDataChannels( - viewContext: ViewContext -) { - const loadedVectorSpecificationsAndRealizationData = viewContext.useViewAtomValue( - "loadedVectorSpecificationsAndRealizationData" - ); - const activeTimestampUtcMs = viewContext.useViewAtomValue("activeTimestampUtcMs"); - const isQueryFetching = viewContext.useViewAtomValue("queryIsFetching"); +export function usePublishToDataChannels(viewContext: ViewContext) { + const loadedVectorSpecificationsAndRealizationData = useAtomValue(loadedVectorSpecificationsAndRealizationDataAtom); + const activeTimestampUtcMs = useAtomValue(activeTimestampUtcMsAtom); + const isQueryFetching = useAtomValue(queryIsFetchingAtom); const makeEnsembleDisplayName = useMakeEnsembleDisplayNameFunc(viewContext); diff --git a/frontend/src/modules/SimulationTimeSeries/view/hooks/useSubplotBuilder.ts b/frontend/src/modules/SimulationTimeSeries/view/hooks/useSubplotBuilder.ts index b9a21c0e7..26462e576 100644 --- a/frontend/src/modules/SimulationTimeSeries/view/hooks/useSubplotBuilder.ts +++ b/frontend/src/modules/SimulationTimeSeries/view/hooks/useSubplotBuilder.ts @@ -2,16 +2,21 @@ import { SummaryVectorObservations_api } from "@api"; import { ViewContext } from "@framework/ModuleContext"; import { ColorSet } from "@lib/utils/ColorSet"; import { Size2D } from "@lib/utils/geometry"; -import { SettingsAtoms } from "@modules/SimulationTimeSeries/settings/atoms/atomDefinitions"; -import { SettingsToViewInterface } from "@modules/SimulationTimeSeries/settingsToViewInterface"; -import { State } from "@modules/SimulationTimeSeries/state"; +import { Interfaces } from "@modules/SimulationTimeSeries/interfaces"; +import { useAtomValue } from "jotai"; import { Layout } from "plotly.js"; import { useMakeEnsembleDisplayNameFunc } from "./useMakeEnsembleDisplayNameFunc"; import { GroupBy, VectorSpec, VisualizationMode } from "../../typesAndEnums"; -import { ViewAtoms } from "../atoms/atomDefinitions"; +import { + activeTimestampUtcMsAtom, + loadedVectorSpecificationsAndHistoricalDataAtom, + loadedVectorSpecificationsAndRealizationDataAtom, + loadedVectorSpecificationsAndStatisticsDataAtom, +} from "../atoms/derivedAtoms"; +import { vectorObservationsQueriesAtom } from "../atoms/queryAtoms"; import { EnsemblesContinuousParameterColoring } from "../utils/ensemblesContinuousParameterColoring"; import { SubplotBuilder, SubplotOwner } from "../utils/subplotBuilder"; import { TimeSeriesPlotData } from "../utils/timeSeriesPlotData"; @@ -21,7 +26,7 @@ import { } from "../utils/vectorSpecificationsAndQueriesUtils"; export function useSubplotBuilder( - viewContext: ViewContext, + viewContext: ViewContext, wrapperDivSize: Size2D, colorSet: ColorSet, ensemblesParameterColoring: EnsemblesContinuousParameterColoring | null @@ -33,18 +38,12 @@ export function useSubplotBuilder( const showHistorical = viewContext.useSettingsToViewInterfaceValue("showHistorical"); const statisticsSelection = viewContext.useSettingsToViewInterfaceValue("statisticsSelection"); - const vectorObservationsQueries = viewContext.useViewAtomValue("vectorObservationsQueries"); - const loadedVectorSpecificationsAndRealizationData = viewContext.useViewAtomValue( - "loadedVectorSpecificationsAndRealizationData" - ); - const loadedVectorSpecificationsAndStatisticsData = viewContext.useViewAtomValue( - "loadedVectorSpecificationsAndStatisticsData" - ); - const loadedVectorSpecificationsAndHistoricalData = viewContext.useViewAtomValue( - "loadedVectorSpecificationsAndHistoricalData" - ); + const vectorObservationsQueries = useAtomValue(vectorObservationsQueriesAtom); + const loadedVectorSpecificationsAndRealizationData = useAtomValue(loadedVectorSpecificationsAndRealizationDataAtom); + const loadedVectorSpecificationsAndStatisticsData = useAtomValue(loadedVectorSpecificationsAndStatisticsDataAtom); + const loadedVectorSpecificationsAndHistoricalData = useAtomValue(loadedVectorSpecificationsAndHistoricalDataAtom); const colorByParameter = viewContext.useSettingsToViewInterfaceValue("colorByParameter"); - const activeTimestampUtcMs = viewContext.useViewAtomValue("activeTimestampUtcMs"); + const activeTimestampUtcMs = useAtomValue(activeTimestampUtcMsAtom); const makeEnsembleDisplayName = useMakeEnsembleDisplayNameFunc(viewContext); diff --git a/frontend/src/modules/SimulationTimeSeries/view/view.tsx b/frontend/src/modules/SimulationTimeSeries/view/view.tsx index 522b98a21..8db2987de 100644 --- a/frontend/src/modules/SimulationTimeSeries/view/view.tsx +++ b/frontend/src/modules/SimulationTimeSeries/view/view.tsx @@ -8,21 +8,19 @@ import { useElementSize } from "@lib/hooks/useElementSize"; import { ColorScaleGradientType } from "@lib/utils/ColorScale"; import { ContentError } from "@modules/_shared/components/ContentMessage"; +import { useAtomValue, useSetAtom } from "jotai"; import { PlotDatum, PlotMouseEvent } from "plotly.js"; -import { ViewAtoms } from "./atoms/atomDefinitions"; +import { userSelectedActiveTimestampUtcMsAtom } from "./atoms/baseAtoms"; +import { realizationsQueryHasErrorAtom, statisticsQueryHasErrorAtom } from "./atoms/derivedAtoms"; import { useMakeViewStatusWriterMessages } from "./hooks/useMakeViewStatusWriterMessages"; import { usePublishToDataChannels } from "./hooks/usePublishToDataChannels"; import { useSubplotBuilder } from "./hooks/useSubplotBuilder"; import { EnsemblesContinuousParameterColoring } from "./utils/ensemblesContinuousParameterColoring"; -import { SettingsAtoms } from "../settings/atoms/atomDefinitions"; -import { SettingsToViewInterface } from "../settingsToViewInterface"; +import { Interfaces } from "../interfaces"; -export const View = ({ - viewContext, - workbenchSettings, -}: ModuleViewProps, SettingsToViewInterface, SettingsAtoms, ViewAtoms>) => { +export const View = ({ viewContext, workbenchSettings }: ModuleViewProps) => { const wrapperDivRef = React.useRef(null); const wrapperDivSize = useElementSize(wrapperDivRef); @@ -31,10 +29,10 @@ export const View = ({ const colorByParameter = viewContext.useSettingsToViewInterfaceValue("colorByParameter"); const parameterIdent = viewContext.useSettingsToViewInterfaceValue("parameterIdent"); const selectedEnsembles = viewContext.useSettingsToViewInterfaceValue("selectedEnsembles"); - const hasRealizationsQueryError = viewContext.useViewAtomValue("realizationsQueryHasError"); - const hasStatisticsQueryError = viewContext.useViewAtomValue("statisticsQueryHasError"); + const hasRealizationsQueryError = useAtomValue(realizationsQueryHasErrorAtom); + const hasStatisticsQueryError = useAtomValue(statisticsQueryHasErrorAtom); - const setActiveTimestampUtcMs = viewContext.useSetViewAtom("userSelectedActiveTimestampUtcMs"); + const setActiveTimestampUtcMs = useSetAtom(userSelectedActiveTimestampUtcMsAtom); // Color palettes const colorSet = workbenchSettings.useColorSet(); diff --git a/frontend/src/modules/SimulationTimeSeriesMatrix/channelDefs.ts b/frontend/src/modules/SimulationTimeSeriesMatrix/channelDefs.ts deleted file mode 100644 index 84709f6b4..000000000 --- a/frontend/src/modules/SimulationTimeSeriesMatrix/channelDefs.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ChannelDefinition, KeyKind } from "@framework/DataChannelTypes"; - -export enum ChannelIds { - TIME_SERIES = "TimeSeries (with value per realization)", -} - -export const channelDefs: ChannelDefinition[] = [ - { - idString: ChannelIds.TIME_SERIES, - displayName: "Time series (with value per realization)", - kindOfKey: KeyKind.REALIZATION, - }, -]; diff --git a/frontend/src/modules/SimulationTimeSeriesMatrix/dataGenerators.ts b/frontend/src/modules/SimulationTimeSeriesMatrix/dataGenerators.ts deleted file mode 100644 index db8ebc259..000000000 --- a/frontend/src/modules/SimulationTimeSeriesMatrix/dataGenerators.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { VectorRealizationData_api } from "@api"; -import { ChannelContentMetaData, DataGenerator } from "@framework/DataChannelTypes"; -import { EnsembleIdent } from "@framework/EnsembleIdent"; -import { simulationUnitReformat, simulationVectorDescription } from "@modules/_shared/reservoirSimulationStringUtils"; - -import { VectorSpec } from "./state"; - -export function makeVectorGroupDataGenerator( - vectorSpecification: VectorSpec, - vectorSpecificationsAndRealizationData: { - vectorSpecification: VectorSpec; - data: VectorRealizationData_api[]; - }[], - activeTimestampUtcMs: number, - makeEnsembleDisplayName: (ensembleIdent: EnsembleIdent) => string -): DataGenerator { - return () => { - const data: { key: number; value: number }[] = []; - let metaData: ChannelContentMetaData = { - unit: "", - ensembleIdentString: "", - displayString: "", - }; - - const vector = vectorSpecificationsAndRealizationData.find( - (vec) => - vec.vectorSpecification.vectorName === vectorSpecification.vectorName && - vec.vectorSpecification.ensembleIdent.equals(vectorSpecification.ensembleIdent) - ); - if (vector) { - let unit = ""; - vector.data.forEach((el) => { - unit = simulationUnitReformat(el.unit); - const indexOfTimestamp = el.timestamps_utc_ms.indexOf(activeTimestampUtcMs); - data.push({ - key: el.realization, - value: indexOfTimestamp === -1 ? el.values[0] : el.values[indexOfTimestamp], - }); - }); - metaData = { - unit, - ensembleIdentString: vector.vectorSpecification.ensembleIdent.toString(), - displayString: `${simulationVectorDescription( - vector.vectorSpecification.vectorName - )} (${makeEnsembleDisplayName(vector.vectorSpecification.ensembleIdent)})`, - }; - } - return { - data, - metaData: metaData ?? undefined, - }; - }; -} diff --git a/frontend/src/modules/SimulationTimeSeriesMatrix/loadModule.tsx b/frontend/src/modules/SimulationTimeSeriesMatrix/loadModule.tsx deleted file mode 100644 index 26f47cd83..000000000 --- a/frontend/src/modules/SimulationTimeSeriesMatrix/loadModule.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Frequency_api, StatisticFunction_api } from "@api"; -import { ModuleRegistry } from "@framework/ModuleRegistry"; - -import { Settings } from "./settings"; -import { FanchartStatisticOption, GroupBy, State, VisualizationMode } from "./state"; -import { View } from "./view"; - -const defaultState: State = { - groupBy: GroupBy.TIME_SERIES, - colorRealizationsByParameter: false, - parameterIdent: null, - visualizationMode: VisualizationMode.STATISTICAL_FANCHART, - vectorSpecifications: [], - resamplingFrequency: Frequency_api.MONTHLY, - showObservations: true, - showHistorical: true, - statisticsSelection: { - IndividualStatisticsSelection: Object.values(StatisticFunction_api), - FanchartStatisticsSelection: Object.values(FanchartStatisticOption), - }, -}; - -const module = ModuleRegistry.initModule("SimulationTimeSeriesMatrix", defaultState); - -module.viewFC = View; -module.settingsFC = Settings; diff --git a/frontend/src/modules/SimulationTimeSeriesMatrix/preview.svg b/frontend/src/modules/SimulationTimeSeriesMatrix/preview.svg deleted file mode 100644 index f87d9840c..000000000 --- a/frontend/src/modules/SimulationTimeSeriesMatrix/preview.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/frontend/src/modules/SimulationTimeSeriesMatrix/preview.tsx b/frontend/src/modules/SimulationTimeSeriesMatrix/preview.tsx deleted file mode 100644 index eb2960245..000000000 --- a/frontend/src/modules/SimulationTimeSeriesMatrix/preview.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { DrawPreviewFunc } from "@framework/Preview"; -import previewImg from "./preview.svg"; - -export const preview: DrawPreviewFunc = function (width: number, height: number) { - return -}; diff --git a/frontend/src/modules/SimulationTimeSeriesMatrix/queryHooks.tsx b/frontend/src/modules/SimulationTimeSeriesMatrix/queryHooks.tsx deleted file mode 100644 index 5a414f713..000000000 --- a/frontend/src/modules/SimulationTimeSeriesMatrix/queryHooks.tsx +++ /dev/null @@ -1,225 +0,0 @@ -import { Frequency_api, SummaryVectorObservations_api, VectorDescription_api } from "@api"; -import { VectorHistoricalData_api, VectorRealizationData_api, VectorStatisticData_api } from "@api"; -import { apiService } from "@framework/ApiService"; -import { EnsembleIdent } from "@framework/EnsembleIdent"; -import { UseQueryResult, useQueries } from "@tanstack/react-query"; - -import { VectorSpec } from "./state"; - -const STALE_TIME = 60 * 1000; -const CACHE_TIME = 60 * 1000; - -export function useVectorListQueries( - caseUuidsAndEnsembleNames: EnsembleIdent[] | null -): UseQueryResult[] { - return useQueries({ - queries: (caseUuidsAndEnsembleNames ?? []).map((item) => { - return { - queryKey: ["getVectorList", item.getCaseUuid(), item.getEnsembleName()], - queryFn: () => - apiService.timeseries.getVectorList(item.getCaseUuid() ?? "", item.getEnsembleName() ?? ""), - staleTime: STALE_TIME, - gcTime: CACHE_TIME, - enabled: item.getCaseUuid() && item.getEnsembleName() ? true : false, - }; - }), - }); -} - -export function useVectorDataQueries( - vectorSpecifications: VectorSpec[] | null, - resampleFrequency: Frequency_api | null, - allowEnable: boolean -): UseQueryResult[] { - return useQueries({ - queries: (vectorSpecifications ?? []).map((item) => { - return { - queryKey: [ - "getRealizationsVectorData", - item.ensembleIdent.getCaseUuid(), - item.ensembleIdent.getEnsembleName(), - item.vectorName, - resampleFrequency, - item.selectedIndividualRealizations, - ], - queryFn: () => - apiService.timeseries.getRealizationsVectorData( - item.ensembleIdent.getCaseUuid() ?? "", - item.ensembleIdent.getEnsembleName() ?? "", - item.vectorName ?? "", - resampleFrequency ?? undefined, - item.selectedIndividualRealizations - ), - staleTime: STALE_TIME, - gcTime: CACHE_TIME, - enabled: !!( - allowEnable && - item.vectorName && - item.ensembleIdent.getCaseUuid() && - item.ensembleIdent.getEnsembleName() - ), - }; - }), - }); -} - -export function useStatisticalVectorDataQueries( - vectorSpecifications: VectorSpec[] | null, - resampleFrequency: Frequency_api | null, - allowEnable: boolean -): UseQueryResult[] { - return useQueries({ - queries: (vectorSpecifications ?? []).map((item) => { - return { - queryKey: [ - "getStatisticalVectorData", - item.ensembleIdent.getCaseUuid(), - item.ensembleIdent.getEnsembleName(), - item.vectorName, - resampleFrequency, - item.selectedStatisticsRealizations, - ], - queryFn: () => - apiService.timeseries.getStatisticalVectorData( - item.ensembleIdent.getCaseUuid() ?? "", - item.ensembleIdent.getEnsembleName() ?? "", - item.vectorName ?? "", - resampleFrequency ?? Frequency_api.MONTHLY, - undefined, - item.selectedStatisticsRealizations - ), - staleTime: STALE_TIME, - gcTime: CACHE_TIME, - enabled: !!( - allowEnable && - item.vectorName && - item.ensembleIdent.getCaseUuid() && - item.ensembleIdent.getEnsembleName() && - resampleFrequency - ), - }; - }), - }); -} - -export function useHistoricalVectorDataQueries( - nonHistoricalVectorSpecifications: VectorSpec[] | null, - resampleFrequency: Frequency_api | null, - allowEnable: boolean -): UseQueryResult[] { - return useQueries({ - queries: (nonHistoricalVectorSpecifications ?? []).map((item) => { - return { - queryKey: [ - "getHistoricalVectorData", - item.ensembleIdent.getCaseUuid(), - item.ensembleIdent.getEnsembleName(), - item.vectorName, - resampleFrequency, - ], - queryFn: () => - apiService.timeseries.getHistoricalVectorData( - item.ensembleIdent.getCaseUuid() ?? "", - item.ensembleIdent.getEnsembleName() ?? "", - item.vectorName ?? "", - resampleFrequency ?? Frequency_api.MONTHLY - ), - staleTime: STALE_TIME, - gcTime: CACHE_TIME, - enabled: !!( - allowEnable && - item.vectorName && - item.ensembleIdent.getCaseUuid() && - item.ensembleIdent.getEnsembleName() && - resampleFrequency - ), - }; - }), - }); -} - -/** - * Definition of ensemble vector observation data - * - * hasSummaryObservations: true if the ensemble has observations, i.e the summary observations array is not empty - * vectorsObservationData: array of vector observation data for requested vector specifications - */ -export type EnsembleVectorObservationData = { - hasSummaryObservations: boolean; - vectorsObservationData: { vectorSpecification: VectorSpec; data: SummaryVectorObservations_api }[]; -}; - -/** - * Definition of map of ensemble ident and ensemble vector observation data - */ -export type EnsembleVectorObservationDataMap = Map; - -/** - * Definition of vector observations queries result for combined queries - */ -export type VectorObservationsQueriesResult = { - isFetching: boolean; - isError: boolean; - ensembleVectorObservationDataMap: EnsembleVectorObservationDataMap; -}; - -/** - * This function takes vectorSpecifications and returns a map of ensembleIdent and the respective observations data for - * the selected vectors. - * - * If the returned summary array from back-end is empty array, the ensemble does not have observations. - * If the selected vectors are not among the returned summary array, the vector does not have observations. - */ -export function useVectorObservationsQueries( - vectorSpecifications: VectorSpec[] | null, - allowEnable: boolean -): VectorObservationsQueriesResult { - const uniqueEnsembleIdents = [...new Set(vectorSpecifications?.map((item) => item.ensembleIdent) ?? [])]; - return useQueries({ - queries: (uniqueEnsembleIdents ?? []).map((item) => { - return { - queryKey: ["getObservations", item.getCaseUuid()], - queryFn: () => apiService.observations.getObservations(item.getCaseUuid() ?? ""), - staleTime: STALE_TIME, - cacheTime: CACHE_TIME, - enabled: !!(allowEnable && item.getCaseUuid()), - }; - }), - combine: (results) => { - const combinedResult: EnsembleVectorObservationDataMap = new Map(); - if (!vectorSpecifications) - return { isFetching: false, isError: false, ensembleVectorObservationDataMap: combinedResult }; - - results.forEach((result, index) => { - const ensembleIdent = uniqueEnsembleIdents.at(index); - if (!ensembleIdent) return; - - const ensembleVectorSpecifications = vectorSpecifications.filter( - (item) => item.ensembleIdent === ensembleIdent - ); - - const ensembleHasObservations = result.data?.summary.length !== 0; - combinedResult.set(ensembleIdent, { - hasSummaryObservations: ensembleHasObservations, - vectorsObservationData: [], - }); - for (const vectorSpec of ensembleVectorSpecifications) { - const vectorObservationsData = - result.data?.summary.find((elm) => elm.vector_name === vectorSpec.vectorName) ?? null; - if (!vectorObservationsData) continue; - - combinedResult.get(ensembleIdent)?.vectorsObservationData.push({ - vectorSpecification: vectorSpec, - data: vectorObservationsData, - }); - } - }); - - return { - isFetching: results.some((result) => result.isFetching), - isError: results.some((result) => result.isError), - ensembleVectorObservationDataMap: combinedResult, - }; - }, - }); -} diff --git a/frontend/src/modules/SimulationTimeSeriesMatrix/registerModule.ts b/frontend/src/modules/SimulationTimeSeriesMatrix/registerModule.ts deleted file mode 100644 index 354602cbc..000000000 --- a/frontend/src/modules/SimulationTimeSeriesMatrix/registerModule.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ModuleCategory, ModuleDevState } from "@framework/Module"; -import { ModuleDataTagId } from "@framework/ModuleDataTags"; -import { ModuleRegistry } from "@framework/ModuleRegistry"; - -import { channelDefs } from "./channelDefs"; -// import { SyncSettingKey } from "@framework/SyncSettings"; -// import { broadcastChannelsDef } from "./channelDefs"; -import { preview } from "./preview"; -import { State } from "./state"; - -const description = "Plotting of simulation time series data."; - -ModuleRegistry.registerModule({ - moduleName: "SimulationTimeSeriesMatrix", - defaultTitle: "Simulation Time Series Matrix", - category: ModuleCategory.MAIN, - devState: ModuleDevState.DEPRECATED, - dataTagIds: [ModuleDataTagId.SUMMARY, ModuleDataTagId.OBSERVATIONS], - // syncableSettingKeys: [SyncSettingKey.ENSEMBLE, SyncSettingKey.TIME_SERIES], - preview, - channelDefinitions: channelDefs, - description, -}); diff --git a/frontend/src/modules/SimulationTimeSeriesMatrix/settings.tsx b/frontend/src/modules/SimulationTimeSeriesMatrix/settings.tsx deleted file mode 100644 index 5ac23f1c8..000000000 --- a/frontend/src/modules/SimulationTimeSeriesMatrix/settings.tsx +++ /dev/null @@ -1,520 +0,0 @@ -import React from "react"; - -import { Frequency_api, StatisticFunction_api } from "@api"; -import { EnsembleIdent } from "@framework/EnsembleIdent"; -import { Parameter, ParameterIdent } from "@framework/EnsembleParameters"; -import { EnsembleSet } from "@framework/EnsembleSet"; -import { ModuleSettingsProps } from "@framework/Module"; -import { useSettingsStatusWriter } from "@framework/StatusWriter"; -import { useEnsembleSet } from "@framework/WorkbenchSession"; -import { EnsembleSelect } from "@framework/components/EnsembleSelect"; -import { ParameterListFilter } from "@framework/components/ParameterListFilter"; -import { VectorSelector, createVectorSelectorDataFromVectors } from "@framework/components/VectorSelector"; -import { fixupEnsembleIdents } from "@framework/utils/ensembleUiHelpers"; -import { Checkbox } from "@lib/components/Checkbox"; -import { CircularProgress } from "@lib/components/CircularProgress"; -import { CollapsibleGroup } from "@lib/components/CollapsibleGroup"; -import { Dropdown } from "@lib/components/Dropdown"; -import { Label } from "@lib/components/Label"; -import { PendingWrapper } from "@lib/components/PendingWrapper"; -import { QueriesErrorCriteria, QueryStateWrapper } from "@lib/components/QueryStateWrapper"; -import { RadioGroup } from "@lib/components/RadioGroup"; -import { Select } from "@lib/components/Select"; -import { SmartNodeSelectorSelection, TreeDataNode } from "@lib/components/SmartNodeSelector"; -import { useValidState } from "@lib/hooks/useValidState"; -import { resolveClassNames } from "@lib/utils/resolveClassNames"; -import { FilterAlt } from "@mui/icons-material"; - -import { isEqual } from "lodash"; -import { VectorDescription_api } from "src/api"; - -import { useVectorListQueries } from "./queryHooks"; -import { - FanchartStatisticOption, - FanchartStatisticOptionEnumToStringMapping, - FrequencyEnumToStringMapping, - GroupBy, - GroupByEnumToStringMapping, - State, - StatisticFunctionEnumToStringMapping, - VectorSpec, - VisualizationMode, - VisualizationModeEnumToStringMapping, -} from "./state"; -import { EnsembleVectorListsHelper } from "./utils/ensemblesVectorListHelper"; -import { getContinuousAndNonConstantParameters } from "./utils/getContinuousAndNonConstantParameters"; -import { joinStringArrayToHumanReadableString } from "./utils/stringUtils"; - -enum StatisticsType { - INDIVIDUAL = "Individual", - FANCHART = "Fanchart", -} - -export function Settings({ settingsContext, workbenchSession }: ModuleSettingsProps) { - const ensembleSet = useEnsembleSet(workbenchSession); - const statusWriter = useSettingsStatusWriter(settingsContext); - - // Store state/values - const [resampleFrequency, setResamplingFrequency] = settingsContext.useStoreState("resamplingFrequency"); - const [groupBy, setGroupBy] = settingsContext.useStoreState("groupBy"); - const [colorRealizationsByParameter, setColorRealizationsByParameter] = - settingsContext.useStoreState("colorRealizationsByParameter"); - const [visualizationMode, setVisualizationMode] = settingsContext.useStoreState("visualizationMode"); - const [showHistorical, setShowHistorical] = settingsContext.useStoreState("showHistorical"); - const [showObservations, setShowObservations] = settingsContext.useStoreState("showObservations"); - const [statisticsSelection, setStatisticsSelection] = settingsContext.useStoreState("statisticsSelection"); - const setParameterIdent = settingsContext.useSetStoreValue("parameterIdent"); - const setVectorSpecifications = settingsContext.useSetStoreValue("vectorSpecifications"); - - // Transitions - const [isPendingGetParameters, startGetParametersTransition] = React.useTransition(); - - // States - const [previousEnsembleSet, setPreviousEnsembleSet] = React.useState(ensembleSet); - const [selectedEnsembleIdents, setSelectedEnsembleIdents] = React.useState([]); - const [selectedVectorNames, setSelectedVectorNames] = React.useState([]); - const [selectedVectorTags, setSelectedVectorTags] = React.useState([]); - const [availableVectorNames, setAvailableVectorNames] = React.useState([]); - const [vectorSelectorData, setVectorSelectorData] = React.useState([]); - const [statisticsType, setStatisticsType] = React.useState(StatisticsType.INDIVIDUAL); - const [filteredParameterIdentList, setFilteredParameterIdentList] = React.useState([]); - const [prevVectorQueriesDataList, setPrevVectorQueriesDataList] = React.useState< - (VectorDescription_api[] | undefined)[] - >([]); - const [prevSelectedEnsembleIdents, setPrevSelectedEnsembleIdents] = React.useState([]); - // If the selectedEnsembleIdents state gets an initial value, this should also be set to an initial value - // NOTE: DO NOT call a function in order to set the initial value, as this will cause the function to be called - // on every render. Rather set it the same place as where the initial value of selectedEnsembleIdents is set. - const [continuousAndNonConstantParametersUnion, setContinuousAndNonConstantParametersUnion] = React.useState< - Parameter[] - >([]); - - const ensembleVectorListsHelper = React.useRef(new EnsembleVectorListsHelper([], [])); - - if (!isEqual(ensembleSet, previousEnsembleSet)) { - const newSelectedEnsembleIdents = selectedEnsembleIdents.filter((ensemble) => - ensembleSet.hasEnsemble(ensemble) - ); - const validatedEnsembleIdents = fixupEnsembleIdents(newSelectedEnsembleIdents, ensembleSet) ?? []; - if (!isEqual(selectedEnsembleIdents, validatedEnsembleIdents)) { - setSelectedEnsembleIdents(validatedEnsembleIdents); - } - - setPreviousEnsembleSet(ensembleSet); - } - - const vectorListQueries = useVectorListQueries(selectedEnsembleIdents); - - if ( - !isEqual( - vectorListQueries.map((el) => el.data), - prevVectorQueriesDataList - ) || - !isEqual(selectedEnsembleIdents, prevSelectedEnsembleIdents) - ) { - setPrevVectorQueriesDataList(vectorListQueries.map((el) => el.data)); - setPrevSelectedEnsembleIdents(selectedEnsembleIdents); - ensembleVectorListsHelper.current = new EnsembleVectorListsHelper(selectedEnsembleIdents, vectorListQueries); - } - - const isVectorListQueriesFetching = vectorListQueries.some((query) => query.isFetching); - - // Await update of vectorSelectorData until all vector lists are finished fetching - let computedVectorSelectorData = vectorSelectorData; - let computedAvailableVectorNames = availableVectorNames; - const vectorNamesUnion = ensembleVectorListsHelper.current.vectorsUnion(); - if (!isVectorListQueriesFetching && !isEqual(computedAvailableVectorNames, vectorNamesUnion)) { - computedAvailableVectorNames = vectorNamesUnion; - computedVectorSelectorData = createVectorSelectorDataFromVectors(vectorNamesUnion); - - setAvailableVectorNames(computedAvailableVectorNames); - setVectorSelectorData(computedVectorSelectorData); - } - - const selectedVectorNamesHasHistorical = - !isVectorListQueriesFetching && ensembleVectorListsHelper.current.hasAnyHistoricalVector(selectedVectorNames); - - const [selectedParameterIdentStr, setSelectedParameterIdentStr] = useValidState({ - initialState: null, - validStates: filteredParameterIdentList.map((item: ParameterIdent) => item.toString()), - }); - - // Set error if all vector list queries fail - const hasEveryVectorListQueryError = - vectorListQueries.length > 0 && vectorListQueries.every((query) => query.isError); - if (hasEveryVectorListQueryError) { - let errorMessage = "Could not load vectors for selected ensemble"; - if (vectorListQueries.length > 1) { - errorMessage += "s"; - } - statusWriter.addError(errorMessage); - } - - // Set warning for vector names not existing in a selected ensemble - const validateVectorNamesInEnsemble = (vectorNames: string[], ensembleIdent: EnsembleIdent) => { - const existingVectors = vectorNames.filter((vector) => - ensembleVectorListsHelper.current.isVectorInEnsemble(ensembleIdent, vector) - ); - if (existingVectors.length === vectorNames.length) { - return; - } - - const nonExistingVectors = vectorNames.filter((vector) => !existingVectors.includes(vector)); - const ensembleStr = ensembleSet.findEnsemble(ensembleIdent)?.getDisplayName() ?? ensembleIdent.toString(); - const vectorArrayStr = joinStringArrayToHumanReadableString(nonExistingVectors); - statusWriter.addWarning(`Vector ${vectorArrayStr} does not exist in ensemble ${ensembleStr}`); - }; - - // Note: selectedVectorNames is not updated until vectorSelectorData is updated and VectorSelector triggers onChange - if (selectedEnsembleIdents.length === 1) { - // If single ensemble is selected and no vectors exist, selectedVectorNames is empty as no vectors are valid - // in the VectorSelector. Then utilizing selectedVectorTags for status message - const vectorNames = selectedVectorNames.length > 0 ? selectedVectorNames : selectedVectorTags; - validateVectorNamesInEnsemble(vectorNames, selectedEnsembleIdents[0]); - } - for (const ensembleIdent of selectedEnsembleIdents) { - validateVectorNamesInEnsemble(selectedVectorNames, ensembleIdent); - } - - // Set statistics type for checkbox rendering - const computedStatisticsType = computeStatisticsType(statisticsType, visualizationMode); - if (statisticsType !== computedStatisticsType) { - setStatisticsType(computedStatisticsType); - } - - const numberOfQueriesWithData = ensembleVectorListsHelper.current.numberOfQueriesWithData(); - - React.useEffect( - function propagateVectorSpecsToView() { - const newVectorSpecifications: VectorSpec[] = []; - for (const ensembleIdent of selectedEnsembleIdents) { - for (const vector of selectedVectorNames) { - if (!ensembleVectorListsHelper.current.isVectorInEnsemble(ensembleIdent, vector)) { - continue; - } - - newVectorSpecifications.push({ - ensembleIdent: ensembleIdent, - color: ensembleSet.findEnsemble(ensembleIdent)?.getColor() ?? null, - vectorName: vector, - hasHistoricalVector: ensembleVectorListsHelper.current.hasHistoricalVector( - ensembleIdent, - vector - ), - }); - } - } - setVectorSpecifications(newVectorSpecifications); - }, - [selectedEnsembleIdents, selectedVectorNames, numberOfQueriesWithData, setVectorSpecifications, ensembleSet] - ); - - React.useEffect( - function propagateParameterIdentToView() { - if (selectedParameterIdentStr === null) { - setParameterIdent(null); - return; - } - - // Try/catch as ParameterIdent.fromString() can throw - try { - const newParameterIdent = ParameterIdent.fromString(selectedParameterIdentStr); - const isParameterAmongFiltered = filteredParameterIdentList.some((parameter) => - parameter.equals(newParameterIdent) - ); - if (isParameterAmongFiltered) { - setParameterIdent(newParameterIdent); - } else { - setParameterIdent(null); - } - } catch { - setParameterIdent(null); - } - }, - [selectedParameterIdentStr, filteredParameterIdentList, setParameterIdent] - ); - - function handleGroupByChange(event: React.ChangeEvent) { - setGroupBy(event.target.value as GroupBy); - } - - function handleColorByParameterChange(parameterIdentStrings: string[]) { - if (parameterIdentStrings.length !== 0) { - setSelectedParameterIdentStr(parameterIdentStrings[0]); - return; - } - setSelectedParameterIdentStr(null); - } - - function handleEnsembleSelectChange(ensembleIdentArr: EnsembleIdent[]) { - setSelectedEnsembleIdents(ensembleIdentArr); - - startGetParametersTransition(function transitionToGetContinuousAndNonConstantParameters() { - setContinuousAndNonConstantParametersUnion( - getContinuousAndNonConstantParameters(ensembleIdentArr, ensembleSet) - ); - }); - } - - function handleVectorSelectionChange(selection: SmartNodeSelectorSelection) { - setSelectedVectorNames(selection.selectedNodes); - setSelectedVectorTags(selection.selectedTags); - } - - function handleFrequencySelectionChange(newFrequencyStr: string) { - const newFreq = newFrequencyStr !== "RAW" ? (newFrequencyStr as Frequency_api) : null; - setResamplingFrequency(newFreq); - } - - function handleShowHistorical(event: React.ChangeEvent) { - setShowHistorical(event.target.checked); - } - - function handleShowObservations(event: React.ChangeEvent) { - setShowObservations(event.target.checked); - } - - function handleVisualizationModeChange(event: React.ChangeEvent) { - setVisualizationMode(event.target.value as VisualizationMode); - } - - function handleFanchartStatisticsSelectionChange( - event: React.ChangeEvent, - statistic: FanchartStatisticOption - ) { - setStatisticsSelection((prev) => { - if (event.target.checked) { - return { - IndividualStatisticsSelection: prev.IndividualStatisticsSelection, - FanchartStatisticsSelection: prev.FanchartStatisticsSelection - ? [...prev.FanchartStatisticsSelection, statistic] - : [statistic], - }; - } else { - return { - IndividualStatisticsSelection: prev.IndividualStatisticsSelection, - FanchartStatisticsSelection: prev.FanchartStatisticsSelection - ? prev.FanchartStatisticsSelection.filter((item) => item !== statistic) - : [], - }; - } - }); - } - - const handleParameterListFilterChange = React.useCallback( - function handleParameterListFilterChange(filteredParameters: Parameter[]) { - const filteredParamIdents = filteredParameters.map((elm) => - ParameterIdent.fromNameAndGroup(elm.name, elm.groupName) - ); - - setFilteredParameterIdentList(filteredParamIdents); - }, - [setFilteredParameterIdentList] - ); - - function handleIndividualStatisticsSelectionChange( - event: React.ChangeEvent, - statistic: StatisticFunction_api - ) { - setStatisticsSelection((prev) => { - if (event.target.checked) { - return { - IndividualStatisticsSelection: prev.IndividualStatisticsSelection - ? [...prev.IndividualStatisticsSelection, statistic] - : [statistic], - FanchartStatisticsSelection: prev.FanchartStatisticsSelection, - }; - } else { - return { - IndividualStatisticsSelection: prev.IndividualStatisticsSelection - ? prev.IndividualStatisticsSelection.filter((item) => item !== statistic) - : [], - FanchartStatisticsSelection: prev.FanchartStatisticsSelection, - }; - } - }); - } - - function makeStatisticCheckboxes() { - if (computedStatisticsType === StatisticsType.FANCHART) { - return Object.values(FanchartStatisticOption).map((value: FanchartStatisticOption) => { - return ( - { - handleFanchartStatisticsSelectionChange(event, value); - }} - /> - ); - }); - } - if (computedStatisticsType === StatisticsType.INDIVIDUAL) { - return Object.values(StatisticFunction_api).map((value: StatisticFunction_api) => { - return ( - { - handleIndividualStatisticsSelectionChange(event, value); - }} - /> - ); - }); - } - - return []; - } - - return ( -
- - { - return { value: val, label: GroupByEnumToStringMapping[val] }; - })} - onChange={handleGroupByChange} - /> - - - { - return { value: val, label: FrequencyEnumToStringMapping[val] }; - }), - ]} - value={resampleFrequency ?? Frequency_api.MONTHLY} - onChange={handleFrequencySelectionChange} - /> - - - - - - - -
query.isLoading), - })} - > - } - showErrorWhen={QueriesErrorCriteria.ALL_QUERIES_HAVE_ERROR} - errorComponent={"Could not load vectors for selected ensembles"} - > - - -
-
- - { - setColorRealizationsByParameter(event.target.checked); - }} - /> -
-
- } - > - - - - -
- - - - - ); -}; diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/settings/components/realizationsSelect.tsx b/frontend/src/modules/StructuralUncertaintyIntersection/settings/components/realizationsSelect.tsx deleted file mode 100644 index 1d34e9e3f..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/settings/components/realizationsSelect.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from "react"; - -import { Button } from "@lib/components/Button"; -import { Label } from "@lib/components/Label"; -import { Select } from "@lib/components/Select"; - -import { isEqual } from "lodash"; - -type RealizationsSelectProps = { - availableRealizations: number[] | null; - selectedRealizations: number[]; // Added as prop - onChange: (values: number[] | null) => void; -}; - -export const RealizationsSelect: React.FC = (props) => { - // Removed useState for selectedRealizations - - if (!isEqual(props.availableRealizations, props.selectedRealizations)) { - const newValues = - props.availableRealizations?.filter((value) => props.selectedRealizations.includes(value)) ?? []; - if (!isEqual(newValues, props.selectedRealizations)) { - props.onChange(newValues); - } - } - - const realOptions = props.availableRealizations?.map((real) => ({ label: `${real}`, value: `${real}` })) ?? []; - - const onChange = (values: string[]) => { - props.onChange(values.map((value) => parseInt(value))); - }; - - return ( - - - - - } - > -
- -
-
-
- - { - return { value: val, label: VisualizationModeEnumToStringMapping[val] }; - })} - onChange={handleVisualizationModeChange} - /> -
- -
-
- - - -
- ); -} - -function fixupSyncedOrSelectedOrFirstWellbore( - syncedWellbore: Wellbore | null, - selectedWellbore: Wellbore | null, - legalWellbores: Wellbore[] -): Wellbore | null { - const allUuids = legalWellbores.map((elm) => elm.uuid); - if (syncedWellbore && allUuids.includes(syncedWellbore.uuid)) { - return syncedWellbore; - } - if (selectedWellbore && allUuids.includes(selectedWellbore.uuid)) { - return selectedWellbore; - } - if (legalWellbores.length !== 0) { - return legalWellbores[0]; - } - return null; -} - -export function createStratigraphyColors(surfaceNames: string[], colorSet: ColorSet): StratigraphyColorMap { - const colorMap: StratigraphyColorMap = {}; - surfaceNames.forEach((surfaceName, index) => { - colorMap[surfaceName] = index === 0 ? colorSet.getFirstColor() : colorSet.getNextColor(); - }); - return colorMap; -} diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/state.ts b/frontend/src/modules/StructuralUncertaintyIntersection/state.ts deleted file mode 100644 index b6412c1ec..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/state.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { StatisticFunction_api } from "@api"; -import { Wellbore } from "@framework/types/wellbore"; - -import { IntersectionSettings, SurfaceSetAddress, VisualizationMode } from "./typesAndEnums"; - -export interface State { - wellboreAddress: Wellbore | null; - SurfaceSetAddress: SurfaceSetAddress | null; - visualizationMode: VisualizationMode; - stratigraphyColorMap: { [name: string]: string }; - statisticFunctions: StatisticFunction_api[]; - intersectionSettings: IntersectionSettings; -} diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/typesAndEnums.ts b/frontend/src/modules/StructuralUncertaintyIntersection/typesAndEnums.ts deleted file mode 100644 index c89108351..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/typesAndEnums.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { StatisticFunction_api } from "@api"; - -export type SeismicAddress = { - caseUuid: string; - ensemble: string; - realizationNumber: number; - attribute: string; - observed: boolean; - timeString?: string; -}; -export type SurfaceSetAddress = { - caseUuid: string; - ensembleName: string; - names: string[]; - attribute: string; - realizationNums: number[] | null; -}; - - -export type IntersectionSettings = { - extension: number; - zScale: number; -}; - -export const StatisticFunctionEnumToStringMapping = { - [StatisticFunction_api.MEAN]: "Mean", - [StatisticFunction_api.MIN]: "Min", - [StatisticFunction_api.MAX]: "Max", - [StatisticFunction_api.P10]: "P10", - [StatisticFunction_api.P50]: "P50", - [StatisticFunction_api.P90]: "P90", -}; - -export enum VisualizationMode { - INDIVIDUAL_REALIZATIONS = "IndividualRealizations", - STATISTICAL_LINES = "StatisticalLines", - STATISTICS_AND_REALIZATIONS = "StatisticsAndRealizations", -} -export const VisualizationModeEnumToStringMapping = { - [VisualizationMode.INDIVIDUAL_REALIZATIONS]: "Individual realizations", - [VisualizationMode.STATISTICAL_LINES]: "Statistical lines", - [VisualizationMode.STATISTICS_AND_REALIZATIONS]: "Statistics + Realizations", -}; -export type StratigraphyColorMap = { [name: string]: string }; diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/view.tsx b/frontend/src/modules/StructuralUncertaintyIntersection/view.tsx deleted file mode 100644 index d4e0767ba..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/view.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import React from "react"; - -import { IntersectionReferenceSystem, Trajectory } from "@equinor/esv-intersection"; -import { ModuleViewProps } from "@framework/Module"; -import { useViewStatusWriter } from "@framework/StatusWriter"; -import { useElementSize } from "@lib/hooks/useElementSize"; -import { - makeExtendedTrajectoryFromTrajectoryXyzPoints, - makeTrajectoryXyzPointsFromWellboreTrajectory, -} from "@modules/SeismicIntersection/utils/esvIntersectionDataConversion"; -import { useWellboreTrajectoriesQuery } from "@modules/_shared/WellBore/queryHooks"; -import { ContentError } from "@modules/_shared/components/ContentMessage"; -import { usePropagateApiErrorToStatusWriter } from "@modules/_shared/hooks/usePropagateApiErrorToStatusWriter"; - -import { isEqual } from "lodash"; - -import Legend from "./components/Legend"; -import { EsvIntersection } from "./components/esvIntersection"; -import { Interfaces } from "./interfaces"; -import { useSampleSurfaceInPointsQueries } from "./queryHooks"; - -export const View = ({ viewContext }: ModuleViewProps) => { - const wrapperDivRef = React.useRef(null); - const wrapperDivSize = useElementSize(wrapperDivRef); - - const statusWriter = useViewStatusWriter(viewContext); - - const surfaceSetAddress = viewContext.useSettingsToViewInterfaceValue("surfaceSetAddress"); - const visualizationMode = viewContext.useSettingsToViewInterfaceValue("visualizationMode"); - const statisticFunctions = viewContext.useSettingsToViewInterfaceValue("statisticFunctions"); - const wellboreAddress = viewContext.useSettingsToViewInterfaceValue("wellboreAddress"); - const intersectionSettings = viewContext.useSettingsToViewInterfaceValue("intersectionSettings"); - const stratigraphyColorMap = viewContext.useSettingsToViewInterfaceValue("stratigraphyColorMap"); - - // Extended wellbore trajectory for creating intersection/fence extended on both sides of wellbore - const [extendedWellboreTrajectory, setExtendedWellboreTrajectory] = React.useState(null); - - const [renderWellboreTrajectoryXyzPoints, setRenderWellboreTrajectoryXyzPoints] = React.useState( - null - ); - const width = wrapperDivSize.width; - const height = wrapperDivSize.height - 100; - - // Get well trajectories query - const getWellTrajectoriesQuery = useWellboreTrajectoriesQuery(wellboreAddress ? [wellboreAddress.uuid] : undefined); - usePropagateApiErrorToStatusWriter(getWellTrajectoriesQuery, statusWriter); - - // Use first trajectory and create polyline for seismic fence query, and extended wellbore trajectory for generating seismic fence image - - if (getWellTrajectoriesQuery.data && getWellTrajectoriesQuery.data.length !== 0) { - const trajectoryXyzPoints = makeTrajectoryXyzPointsFromWellboreTrajectory(getWellTrajectoriesQuery.data[0]); - const newExtendedWellboreTrajectory = makeExtendedTrajectoryFromTrajectoryXyzPoints( - trajectoryXyzPoints, - intersectionSettings.extension - ); - - // If the new extended trajectory is different, update the polyline, but keep the seismic fence image - if (!isEqual(newExtendedWellboreTrajectory, extendedWellboreTrajectory)) { - setExtendedWellboreTrajectory(newExtendedWellboreTrajectory); - } - - // When new well trajectory 3D points are loaded, update the render trajectory and clear the seismic fence image - if (!isEqual(trajectoryXyzPoints, renderWellboreTrajectoryXyzPoints)) { - setRenderWellboreTrajectoryXyzPoints(trajectoryXyzPoints); - } - } - - const x_points = extendedWellboreTrajectory?.points.map((coord) => coord[0]) ?? []; - const y_points = extendedWellboreTrajectory?.points.map((coord) => coord[1]) ?? []; - - const cum_length = extendedWellboreTrajectory - ? IntersectionReferenceSystem.toDisplacement( - extendedWellboreTrajectory.points, - extendedWellboreTrajectory.offset - ).map((coord) => coord[0] - intersectionSettings.extension) - : []; - - const sampleSurfaceInPointsQueries = useSampleSurfaceInPointsQueries( - surfaceSetAddress?.caseUuid ?? "", - surfaceSetAddress?.ensembleName ?? "", - surfaceSetAddress?.names ?? [], - surfaceSetAddress?.attribute ?? "", - surfaceSetAddress?.realizationNums ?? [], - x_points, - y_points, - true - ); - - statusWriter.setLoading(getWellTrajectoriesQuery.isFetching || sampleSurfaceInPointsQueries.isFetching); - // Build up an error string handling multiple errors. e.g. "Error loading well trajectories and seismic fence data" - // Do not useMemo - let errorString = ""; - if (getWellTrajectoriesQuery.isError) { - errorString += "Error loading well trajectories"; - } - - if (errorString !== "") { - statusWriter.addError(errorString); - } - const stratigraphyColorLegendItems = - surfaceSetAddress?.names.map((key) => { - return { color: stratigraphyColorMap[key], label: key }; - }) ?? []; - - return ( -
- {errorString !== "" ? ( - {errorString} - ) : ( - <> - -
- -
- - )} -
- ); -}; diff --git a/frontend/src/modules/registerAllModules.ts b/frontend/src/modules/registerAllModules.ts index 3b12c8256..7b692eb3a 100644 --- a/frontend/src/modules/registerAllModules.ts +++ b/frontend/src/modules/registerAllModules.ts @@ -13,7 +13,6 @@ import "./Rft/registerModule"; import "./SeismicIntersection/registerModule"; import "./SimulationTimeSeries/registerModule"; import "./SimulationTimeSeriesSensitivity/registerModule"; -import "./StructuralUncertaintyIntersection/registerModule"; import "./SubsurfaceMap/registerModule"; import "./TornadoChart/registerModule"; import "./WellCompletions/registerModule"; From f6ccca195d08a00acf8b6ce9203410133d39ce5b Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Mon, 26 Aug 2024 11:38:38 +0200 Subject: [PATCH 4/7] Bump fmu-sumo==1.2.5 (#706) --- backend_py/primary/poetry.lock | 10 +++++----- backend_py/primary/pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend_py/primary/poetry.lock b/backend_py/primary/poetry.lock index c0f418289..fda2cdab3 100644 --- a/backend_py/primary/poetry.lock +++ b/backend_py/primary/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "annotated-types" @@ -793,13 +793,13 @@ files = [ [[package]] name = "fmu-sumo" -version = "1.2.4" +version = "1.2.5" description = "Python package for interacting with Sumo in an FMU setting" optional = false python-versions = ">=3.8" files = [ - {file = "fmu_sumo-1.2.4-py3-none-any.whl", hash = "sha256:d76ce6de25fe1009a78fcbbe6bf9b4cfd3c410398a81d9f4c073bd3a8b29d6a5"}, - {file = "fmu_sumo-1.2.4.tar.gz", hash = "sha256:a9b422e08af271dca4e6293ae94b343329d59f302bbd44c8430a670e0b4be5c7"}, + {file = "fmu_sumo-1.2.5-py3-none-any.whl", hash = "sha256:11e39d548a220a891892b5af4f94bf059a7db36bf345665bcf18a61c935d396f"}, + {file = "fmu_sumo-1.2.5.tar.gz", hash = "sha256:8d9c5f65df070fd90b737d78f9a7e9c1590a1af0ec57d54645b682dd5ece9737"}, ] [package.dependencies] @@ -3513,4 +3513,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "9c20526602b7ab84f5102041476f2af5da6763caba9935954c67db132b3a38ba" +content-hash = "9e4c2116a592ae012bb3be220132f9465b018c80ebfa3b66c963f3c944965fcb" diff --git a/backend_py/primary/pyproject.toml b/backend_py/primary/pyproject.toml index 5d4d11e2e..f28c866de 100644 --- a/backend_py/primary/pyproject.toml +++ b/backend_py/primary/pyproject.toml @@ -20,7 +20,7 @@ numpy = "^1.24.1" orjson = "^3.8.10" pandas = {version = "2.0.1", extras = ["performance"]} httpx = "^0.24.0" -fmu-sumo = "1.2.4" +fmu-sumo = "1.2.5" sumo-wrapper-python = "1.0.9" azure-monitor-opentelemetry = "^1.1.0" requests-toolbelt = "^1.0.0" From 8d75a33ca370dd4409decf1ee35b9143c0fb3db9 Mon Sep 17 00:00:00 2001 From: Sigurd Pettersen Date: Mon, 26 Aug 2024 13:21:05 +0200 Subject: [PATCH 5/7] Removed VTK user session and related code (#696) Co-authored-by: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> --- backend_py/primary/primary/main.py | 2 - .../primary/routers/grid3d/router_vtk.py | 187 ------------ .../primary/routers/grid3d/schemas_vtk.py | 25 -- .../user_session_manager.py | 7 - backend_py/user_grid3d_vtk/Dockerfile | 26 -- backend_py/user_grid3d_vtk/poetry.lock | 271 ------------------ backend_py/user_grid3d_vtk/pyproject.toml | 12 - .../user_grid3d_vtk/fastapi_app.py | 55 ---- backend_py/user_mock/pyproject.toml | 2 +- docker-compose.yml | 12 - frontend/src/api/index.ts | 2 - .../src/api/models/GridIntersectionVtk.ts | 14 - frontend/src/api/models/GridSurfaceVtk.ts | 17 -- frontend/src/api/services/Grid3DService.ts | 164 ----------- frontend/src/modules/_shared/vtkUtils.ts | 235 --------------- frontend/src/modules/registerAllModules.ts | 1 - radixconfig.yml | 9 - 17 files changed, 1 insertion(+), 1040 deletions(-) delete mode 100644 backend_py/primary/primary/routers/grid3d/router_vtk.py delete mode 100644 backend_py/primary/primary/routers/grid3d/schemas_vtk.py delete mode 100644 backend_py/user_grid3d_vtk/Dockerfile delete mode 100644 backend_py/user_grid3d_vtk/poetry.lock delete mode 100644 backend_py/user_grid3d_vtk/pyproject.toml delete mode 100644 backend_py/user_grid3d_vtk/user_grid3d_vtk/fastapi_app.py delete mode 100644 frontend/src/api/models/GridIntersectionVtk.ts delete mode 100644 frontend/src/api/models/GridSurfaceVtk.ts delete mode 100644 frontend/src/modules/_shared/vtkUtils.ts diff --git a/backend_py/primary/primary/main.py b/backend_py/primary/primary/main.py index 4a5e12d7e..441fd3e8a 100644 --- a/backend_py/primary/primary/main.py +++ b/backend_py/primary/primary/main.py @@ -18,7 +18,6 @@ from primary.routers.general import router as general_router from primary.routers.graph.router import router as graph_router from primary.routers.grid3d.router import router as grid3d_router -from primary.routers.grid3d.router_vtk import router as grid3d_router_vtk from primary.routers.group_tree.router import router as group_tree_router from primary.routers.inplace_volumetrics.router import router as inplace_volumetrics_router from primary.routers.observations.router import router as observations_router @@ -79,7 +78,6 @@ def custom_generate_unique_id(route: APIRoute) -> str: app.include_router(parameters_router, prefix="/parameters", tags=["parameters"]) app.include_router(correlations_router, prefix="/correlations", tags=["correlations"]) app.include_router(grid3d_router, prefix="/grid3d", tags=["grid3d"]) -app.include_router(grid3d_router_vtk, prefix="/grid3d", tags=["grid3d"]) app.include_router(group_tree_router, prefix="/group_tree", tags=["group_tree"]) app.include_router(pvt_router, prefix="/pvt", tags=["pvt"]) app.include_router(well_completions_router, prefix="/well_completions", tags=["well_completions"]) diff --git a/backend_py/primary/primary/routers/grid3d/router_vtk.py b/backend_py/primary/primary/routers/grid3d/router_vtk.py deleted file mode 100644 index dba4f65e2..000000000 --- a/backend_py/primary/primary/routers/grid3d/router_vtk.py +++ /dev/null @@ -1,187 +0,0 @@ -from typing import List - -from fastapi import APIRouter, Depends, Query, HTTPException, status -from starlette.requests import Request - -from primary.services.utils.authenticated_user import AuthenticatedUser -from primary.auth.auth_helper import AuthHelper - -from .schemas_vtk import GridSurfaceVtk, GridIntersectionVtk - -router = APIRouter() - -# pylint: disable=unused-argument -# pylint: disable=unused-variable - - -# Primary backend -@router.get("/grid_surface_vtk") -async def grid_surface_vtk( - request: Request, - case_uuid: str = Query(description="Sumo case uuid"), - ensemble_name: str = Query(description="Ensemble name"), - grid_name: str = Query(description="Grid name"), - realization: str = Query(description="Realization"), - authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), -) -> GridSurfaceVtk: - """Get a grid""" - - query_params = { - "case_uuid": case_uuid, - "ensemble_name": ensemble_name, - "grid_name": grid_name, - "realization": int(realization), - } - - # Add query parameters to the request URL - updated_request = Request( - scope={ - "type": "http", - "method": request.method, - "path": request.url.path, - "query_string": request.url.include_query_params(**query_params).query.encode("utf-8"), - "headers": request.headers.raw, - }, - receive=request._receive, # pylint: disable=protected-access - ) - - raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED) - - -@router.get("/grid_parameter_vtk") -async def grid_parameter_vtk( - request: Request, - case_uuid: str = Query(description="Sumo case uuid"), - ensemble_name: str = Query(description="Ensemble name"), - grid_name: str = Query(description="Grid name"), - parameter_name: str = Query(description="Grid parameter"), - realization: str = Query(description="Realization"), - authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), -) -> List[float]: - """Get a grid parameter""" - - query_params = { - "case_uuid": case_uuid, - "ensemble_name": ensemble_name, - "grid_name": grid_name, - "parameter_name": parameter_name, - "realization": int(realization), - } - - # Add query parameters to the request URL - updated_request = Request( - scope={ - "type": "http", - "method": request.method, - "path": request.url.path, - "query_string": request.url.include_query_params(**query_params).query.encode("utf-8"), - "headers": request.headers.raw, - }, - receive=request._receive, # pylint: disable=protected-access - ) - - raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED) - - -@router.get("/grid_parameter_intersection_vtk") -async def grid_parameter_intersection_vtk( - request: Request, - case_uuid: str = Query(description="Sumo case uuid"), - ensemble_name: str = Query(description="Ensemble name"), - grid_name: str = Query(description="Grid name"), - parameter_name: str = Query(description="Grid parameter"), - realization: str = Query(description="Realization"), - authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), -) -> GridIntersectionVtk: - """Get a grid parameter""" - - query_params = { - "case_uuid": case_uuid, - "ensemble_name": ensemble_name, - "grid_name": grid_name, - "parameter_name": parameter_name, - "realization": int(realization), - } - - # Add query parameters to the request URL - updated_request = Request( - scope={ - "type": "http", - "method": request.method, - "path": request.url.path, - "query_string": request.url.include_query_params(**query_params).query.encode("utf-8"), - "headers": request.headers.raw, - }, - receive=request._receive, # pylint: disable=protected-access - ) - - raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED) - - -@router.get("/statistical_grid_parameter_intersection_vtk") -async def statistical_grid_parameter_intersection_vtk( - request: Request, - case_uuid: str = Query(description="Sumo case uuid"), - ensemble_name: str = Query(description="Ensemble name"), - grid_name: str = Query(description="Grid name"), - parameter_name: str = Query(description="Grid parameter"), - realizations: List[str] = Query(description="Realizations"), - authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), -) -> GridIntersectionVtk: - """Get a grid parameter""" - - query_params = { - "case_uuid": case_uuid, - "ensemble_name": ensemble_name, - "grid_name": grid_name, - "parameter_name": parameter_name, - "realizations": [int(realization) for realization in realizations], - } - - # Add query parameters to the request URL - updated_request = Request( - scope={ - "type": "http", - "method": request.method, - "path": request.url.path, - "query_string": request.url.include_query_params(**query_params).query.encode("utf-8"), - "headers": request.headers.raw, - }, - receive=request._receive, # pylint: disable=protected-access - ) - - raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED) - - -@router.get("/statistical_grid_parameter_vtk") -async def statistical_grid_parameter_vtk( - request: Request, - case_uuid: str = Query(description="Sumo case uuid"), - ensemble_name: str = Query(description="Ensemble name"), - grid_name: str = Query(description="Grid name"), - parameter_name: str = Query(description="Grid parameter"), - realizations: List[str] = Query(description="Realizations"), - authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), -) -> List[float]: - """Get a grid parameter""" - - query_params = { - "case_uuid": case_uuid, - "ensemble_name": ensemble_name, - "grid_name": grid_name, - "parameter_name": parameter_name, - "realizations": [int(realization) for realization in realizations], - } - # Add query parameters to the request URL - updated_request = Request( - scope={ - "type": "http", - "method": request.method, - "path": request.url.path, - "query_string": request.url.include_query_params(**query_params).query.encode("utf-8"), - "headers": request.headers.raw, - }, - receive=request._receive, # pylint: disable=protected-access - ) - - raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED) diff --git a/backend_py/primary/primary/routers/grid3d/schemas_vtk.py b/backend_py/primary/primary/routers/grid3d/schemas_vtk.py deleted file mode 100644 index 1e91d0b4d..000000000 --- a/backend_py/primary/primary/routers/grid3d/schemas_vtk.py +++ /dev/null @@ -1,25 +0,0 @@ -from typing import List - -from pydantic import BaseModel -from webviz_pkg.core_utils.b64 import B64FloatArray, B64UintArray - - -class GridSurfaceVtk(BaseModel): - polys_b64arr: B64UintArray - points_b64arr: B64FloatArray - xmin: float - xmax: float - ymin: float - ymax: float - zmin: float - zmax: float - - -class GridIntersectionVtk(BaseModel): - image: str - polyline_x: List[float] - polyline_y: List[float] - x_min: float - x_max: float - y_min: float - y_max: float diff --git a/backend_py/primary/primary/services/user_session_manager/user_session_manager.py b/backend_py/primary/primary/services/user_session_manager/user_session_manager.py index 626a75921..8ec90de3c 100644 --- a/backend_py/primary/primary/services/user_session_manager/user_session_manager.py +++ b/backend_py/primary/primary/services/user_session_manager/user_session_manager.py @@ -20,7 +20,6 @@ class UserComponent(str, Enum): GRID3D_RI = "GRID3D_RI" - GRID3D_VTK = "GRID3D_VTK" MOCK = "MOCK" @@ -53,12 +52,6 @@ class _UserSessionDef: resource_req=RadixResourceRequests(cpu="4", memory="16Gi"), payload_dict={"ri_omp_num_treads": 4}, ), - UserComponent.GRID3D_VTK: _UserSessionDef( - job_component_name="user-grid3d-vtk", - port=8003, - resource_req=RadixResourceRequests(cpu="200m", memory="400Mi"), - payload_dict=None, - ), } diff --git a/backend_py/user_grid3d_vtk/Dockerfile b/backend_py/user_grid3d_vtk/Dockerfile deleted file mode 100644 index 199ffc8d1..000000000 --- a/backend_py/user_grid3d_vtk/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -FROM python:3.11-slim - -RUN useradd --create-home --uid 1234 appuser -USER 1234 - -ENV PATH="${PATH}:/home/appuser/.local/bin" - -RUN python3 -m pip install --user pipx -RUN python3 -m pipx ensurepath -RUN pipx install poetry==1.8.2 - -ENV VIRTUAL_ENV=/home/appuser/venv -RUN python3 -m venv $VIRTUAL_ENV -ENV PATH="$VIRTUAL_ENV/bin:$PATH" - -WORKDIR /home/appuser/backend_py/user_grid3d_vtk - -COPY --chown=appuser ./backend_py/user_grid3d_vtk/pyproject.toml /home/appuser/backend_py/user_grid3d_vtk -COPY --chown=appuser ./backend_py/user_grid3d_vtk/poetry.lock /home/appuser/backend_py/user_grid3d_vtk -RUN poetry install --only main --no-root --no-directory - -COPY --chown=appuser ./backend_py/user_grid3d_vtk/user_grid3d_vtk /home/appuser/backend_py/user_grid3d_vtk/user_grid3d_vtk -RUN poetry install --only main - -# Relevant uvicorn environment variables are: UVICORN_PORT, UVICORN_RELOAD -CMD ["uvicorn", "user_grid3d_vtk.fastapi_app:app", "--host", "0.0.0.0"] diff --git a/backend_py/user_grid3d_vtk/poetry.lock b/backend_py/user_grid3d_vtk/poetry.lock deleted file mode 100644 index 97fcd2ff8..000000000 --- a/backend_py/user_grid3d_vtk/poetry.lock +++ /dev/null @@ -1,271 +0,0 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.6.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -files = [ - {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, - {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, -] - -[[package]] -name = "anyio" -version = "3.7.1" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.7" -files = [ - {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, - {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, -] - -[package.dependencies] -idna = ">=2.8" -sniffio = ">=1.1" - -[package.extras] -doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] -test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (<0.22)"] - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "fastapi" -version = "0.103.2" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -optional = false -python-versions = ">=3.7" -files = [ - {file = "fastapi-0.103.2-py3-none-any.whl", hash = "sha256:3270de872f0fe9ec809d4bd3d4d890c6d5cc7b9611d721d6438f9dacc8c4ef2e"}, - {file = "fastapi-0.103.2.tar.gz", hash = "sha256:75a11f6bfb8fc4d2bec0bd710c2d5f2829659c0e8c0afd5560fdda6ce25ec653"}, -] - -[package.dependencies] -anyio = ">=3.7.1,<4.0.0" -pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.27.0,<0.28.0" -typing-extensions = ">=4.5.0" - -[package.extras] -all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[[package]] -name = "idna" -version = "3.6" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, -] - -[[package]] -name = "pydantic" -version = "2.6.4" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, - {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, -] - -[package.dependencies] -annotated-types = ">=0.4.0" -pydantic-core = "2.16.3" -typing-extensions = ">=4.6.1" - -[package.extras] -email = ["email-validator (>=2.0.0)"] - -[[package]] -name = "pydantic-core" -version = "2.16.3" -description = "" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"}, - {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"}, - {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"}, - {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"}, - {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"}, - {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"}, - {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"}, - {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"}, - {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"}, - {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"}, - {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"}, - {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"}, - {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"}, - {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"}, - {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"}, - {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"}, - {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"}, - {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"}, - {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"}, - {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"}, - {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"}, - {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"}, - {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"}, - {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"}, - {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"}, - {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"}, - {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"}, - {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"}, - {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"}, - {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"}, - {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"}, - {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"}, - {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - -[[package]] -name = "starlette" -version = "0.27.0" -description = "The little ASGI library that shines." -optional = false -python-versions = ">=3.7" -files = [ - {file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"}, - {file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"}, -] - -[package.dependencies] -anyio = ">=3.4.0,<5" - -[package.extras] -full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] - -[[package]] -name = "typing-extensions" -version = "4.10.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, - {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, -] - -[[package]] -name = "uvicorn" -version = "0.20.0" -description = "The lightning-fast ASGI server." -optional = false -python-versions = ">=3.7" -files = [ - {file = "uvicorn-0.20.0-py3-none-any.whl", hash = "sha256:c3ed1598a5668208723f2bb49336f4509424ad198d6ab2615b7783db58d919fd"}, - {file = "uvicorn-0.20.0.tar.gz", hash = "sha256:a4e12017b940247f836bc90b72e725d7dfd0c8ed1c51eb365f5ba30d9f5127d8"}, -] - -[package.dependencies] -click = ">=7.0" -h11 = ">=0.8" - -[package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.11" -content-hash = "1c1900c103a60137142a48f7698b943ffebf2eab40628b1bf69c65fa232af688" diff --git a/backend_py/user_grid3d_vtk/pyproject.toml b/backend_py/user_grid3d_vtk/pyproject.toml deleted file mode 100644 index 53cad62ac..000000000 --- a/backend_py/user_grid3d_vtk/pyproject.toml +++ /dev/null @@ -1,12 +0,0 @@ -[tool.poetry] -package-mode = false -name = "user-grid3d-vtk" - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" - -[tool.poetry.dependencies] -python = "^3.11" -fastapi = "^0.103.1" -uvicorn = "^0.20.0" diff --git a/backend_py/user_grid3d_vtk/user_grid3d_vtk/fastapi_app.py b/backend_py/user_grid3d_vtk/user_grid3d_vtk/fastapi_app.py deleted file mode 100644 index b4de8582d..000000000 --- a/backend_py/user_grid3d_vtk/user_grid3d_vtk/fastapi_app.py +++ /dev/null @@ -1,55 +0,0 @@ -import asyncio -import datetime -import logging -from typing import Annotated - -from fastapi import FastAPI -from fastapi import Query - -logging.basicConfig(format="%(asctime)s %(levelname)-3s [%(name)s]: %(message)s", datefmt="%H:%M:%S") -logging.getLogger().setLevel(logging.DEBUG) - -LOGGER = logging.getLogger(__name__) - - -app = FastAPI() - - -@app.get("/") -async def root() -> str: - ret_str = f"user-grid3d-vtk is alive at this time: {datetime.datetime.now()}" - LOGGER.debug("Sending: ", ret_str) - return ret_str - - -# Probe if service is alive -# HTTP status code 200 means we're alive, all other status codes indicate trouble -# The response is only for debugging, and is basically ignored. -@app.get("/health/live") -async def health_live() -> str: - ret_str = f"LIVE at: {datetime.datetime.now()}" - LOGGER.debug(f"health_live() returning: {ret_str!r}") - return ret_str - - -# Probe if service is ready to receive requests -# HTTP status code 200 means we're ready, 500 (and all other status codes) signals we're not ready -# The response is only for debugging, and is basically ignored. -@app.get("/health/ready") -async def health_ready() -> str: - ret_str = f"READY at: {datetime.datetime.now()}" - LOGGER.debug(f"health_ready() returning: {ret_str!r}") - return ret_str - - -# Simulate doing some work -@app.get("/dowork") -async def dowork( - duration: Annotated[float, Query(description="Duration of work in seconds")] = 1.0, -) -> str: - LOGGER.debug(f"dowork() doing fake GRID3D VTK work for: {duration=}s") - await asyncio.sleep(duration) - - ret_str = f"GRID3D VTK work done at: {datetime.datetime.now()}" - LOGGER.debug(f"dowork() GRID3D VTK returning: {ret_str!r}") - return ret_str diff --git a/backend_py/user_mock/pyproject.toml b/backend_py/user_mock/pyproject.toml index 53cad62ac..740c0d586 100644 --- a/backend_py/user_mock/pyproject.toml +++ b/backend_py/user_mock/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] package-mode = false -name = "user-grid3d-vtk" +name = "user-mock" [build-system] requires = ["poetry-core"] diff --git a/docker-compose.yml b/docker-compose.yml index 89c2287f8..9e69fffef 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -81,18 +81,6 @@ services: - ./backend_py/user_grid3d_ri/user_grid3d_ri:/home/appuser/backend_py/user_grid3d_ri/user_grid3d_ri - ./backend_py/libs:/home/appuser/backend_py/libs - user-grid3d-vtk: - build: - context: . - dockerfile: ./backend_py/user_grid3d_vtk/Dockerfile - ports: - - 8003:8003 - environment: - - UVICORN_PORT=8003 - - UVICORN_RELOAD=true - volumes: - - ./backend_py/user_grid3d_vtk:/home/appuser/backend_py/user_grid3d_vtk - redis-user-session: image: bitnami/redis:6.2.10@sha256:bd42fcdab5959ce2b21b6ea8410d4b3ee87ecb2e320260326ec731ecfcffbd0e expose: diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index 2a591446f..af145a030 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -40,8 +40,6 @@ export type { Grid3dMappedProperty as Grid3dMappedProperty_api } from './models/ export type { Grid3dPropertyInfo as Grid3dPropertyInfo_api } from './models/Grid3dPropertyInfo'; export type { Grid3dZone as Grid3dZone_api } from './models/Grid3dZone'; export type { GridDimensions as GridDimensions_api } from './models/GridDimensions'; -export type { GridIntersectionVtk as GridIntersectionVtk_api } from './models/GridIntersectionVtk'; -export type { GridSurfaceVtk as GridSurfaceVtk_api } from './models/GridSurfaceVtk'; export type { GroupTreeData as GroupTreeData_api } from './models/GroupTreeData'; export type { GroupTreeMetadata as GroupTreeMetadata_api } from './models/GroupTreeMetadata'; export type { HTTPValidationError as HTTPValidationError_api } from './models/HTTPValidationError'; diff --git a/frontend/src/api/models/GridIntersectionVtk.ts b/frontend/src/api/models/GridIntersectionVtk.ts deleted file mode 100644 index 1e4672669..000000000 --- a/frontend/src/api/models/GridIntersectionVtk.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type GridIntersectionVtk = { - image: string; - polyline_x: Array; - polyline_y: Array; - x_min: number; - x_max: number; - y_min: number; - y_max: number; -}; - diff --git a/frontend/src/api/models/GridSurfaceVtk.ts b/frontend/src/api/models/GridSurfaceVtk.ts deleted file mode 100644 index 00065a55d..000000000 --- a/frontend/src/api/models/GridSurfaceVtk.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { B64FloatArray } from './B64FloatArray'; -import type { B64UintArray } from './B64UintArray'; -export type GridSurfaceVtk = { - polys_b64arr: B64UintArray; - points_b64arr: B64FloatArray; - xmin: number; - xmax: number; - ymin: number; - ymax: number; - zmin: number; - zmax: number; -}; - diff --git a/frontend/src/api/services/Grid3DService.ts b/frontend/src/api/services/Grid3DService.ts index cfd1e1f3e..24075137d 100644 --- a/frontend/src/api/services/Grid3DService.ts +++ b/frontend/src/api/services/Grid3DService.ts @@ -6,8 +6,6 @@ import type { Body_post_get_polyline_intersection } from '../models/Body_post_ge import type { Grid3dGeometry } from '../models/Grid3dGeometry'; import type { Grid3dInfo } from '../models/Grid3dInfo'; import type { Grid3dMappedProperty } from '../models/Grid3dMappedProperty'; -import type { GridIntersectionVtk } from '../models/GridIntersectionVtk'; -import type { GridSurfaceVtk } from '../models/GridSurfaceVtk'; import type { PolylineIntersection } from '../models/PolylineIntersection'; import type { CancelablePromise } from '../core/CancelablePromise'; import type { BaseHttpRequest } from '../core/BaseHttpRequest'; @@ -208,166 +206,4 @@ export class Grid3DService { }, }); } - /** - * Grid Surface Vtk - * Get a grid - * @param caseUuid Sumo case uuid - * @param ensembleName Ensemble name - * @param gridName Grid name - * @param realization Realization - * @returns GridSurfaceVtk Successful Response - * @throws ApiError - */ - public gridSurfaceVtk( - caseUuid: string, - ensembleName: string, - gridName: string, - realization: string, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'GET', - url: '/grid3d/grid_surface_vtk', - query: { - 'case_uuid': caseUuid, - 'ensemble_name': ensembleName, - 'grid_name': gridName, - 'realization': realization, - }, - errors: { - 422: `Validation Error`, - }, - }); - } - /** - * Grid Parameter Vtk - * Get a grid parameter - * @param caseUuid Sumo case uuid - * @param ensembleName Ensemble name - * @param gridName Grid name - * @param parameterName Grid parameter - * @param realization Realization - * @returns number Successful Response - * @throws ApiError - */ - public gridParameterVtk( - caseUuid: string, - ensembleName: string, - gridName: string, - parameterName: string, - realization: string, - ): CancelablePromise> { - return this.httpRequest.request({ - method: 'GET', - url: '/grid3d/grid_parameter_vtk', - query: { - 'case_uuid': caseUuid, - 'ensemble_name': ensembleName, - 'grid_name': gridName, - 'parameter_name': parameterName, - 'realization': realization, - }, - errors: { - 422: `Validation Error`, - }, - }); - } - /** - * Grid Parameter Intersection Vtk - * Get a grid parameter - * @param caseUuid Sumo case uuid - * @param ensembleName Ensemble name - * @param gridName Grid name - * @param parameterName Grid parameter - * @param realization Realization - * @returns GridIntersectionVtk Successful Response - * @throws ApiError - */ - public gridParameterIntersectionVtk( - caseUuid: string, - ensembleName: string, - gridName: string, - parameterName: string, - realization: string, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'GET', - url: '/grid3d/grid_parameter_intersection_vtk', - query: { - 'case_uuid': caseUuid, - 'ensemble_name': ensembleName, - 'grid_name': gridName, - 'parameter_name': parameterName, - 'realization': realization, - }, - errors: { - 422: `Validation Error`, - }, - }); - } - /** - * Statistical Grid Parameter Intersection Vtk - * Get a grid parameter - * @param caseUuid Sumo case uuid - * @param ensembleName Ensemble name - * @param gridName Grid name - * @param parameterName Grid parameter - * @param realizations Realizations - * @returns GridIntersectionVtk Successful Response - * @throws ApiError - */ - public statisticalGridParameterIntersectionVtk( - caseUuid: string, - ensembleName: string, - gridName: string, - parameterName: string, - realizations: Array, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'GET', - url: '/grid3d/statistical_grid_parameter_intersection_vtk', - query: { - 'case_uuid': caseUuid, - 'ensemble_name': ensembleName, - 'grid_name': gridName, - 'parameter_name': parameterName, - 'realizations': realizations, - }, - errors: { - 422: `Validation Error`, - }, - }); - } - /** - * Statistical Grid Parameter Vtk - * Get a grid parameter - * @param caseUuid Sumo case uuid - * @param ensembleName Ensemble name - * @param gridName Grid name - * @param parameterName Grid parameter - * @param realizations Realizations - * @returns number Successful Response - * @throws ApiError - */ - public statisticalGridParameterVtk( - caseUuid: string, - ensembleName: string, - gridName: string, - parameterName: string, - realizations: Array, - ): CancelablePromise> { - return this.httpRequest.request({ - method: 'GET', - url: '/grid3d/statistical_grid_parameter_vtk', - query: { - 'case_uuid': caseUuid, - 'ensemble_name': ensembleName, - 'grid_name': gridName, - 'parameter_name': parameterName, - 'realizations': realizations, - }, - errors: { - 422: `Validation Error`, - }, - }); - } } diff --git a/frontend/src/modules/_shared/vtkUtils.ts b/frontend/src/modules/_shared/vtkUtils.ts deleted file mode 100644 index 71e13bc79..000000000 --- a/frontend/src/modules/_shared/vtkUtils.ts +++ /dev/null @@ -1,235 +0,0 @@ -//Based on code by Kitware Inc. (BSD-3-Clause) -// Copyright (c) 2016, Kitware Inc. -// All rights reserved. - -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: - -// - Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. - -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. - -// - Neither the name of the copyright holder nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. - -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -/* eslint-disable no-bitwise */ - -// ---------------------------------------------------------------------------- -// Decoding infrastructure -// ---------------------------------------------------------------------------- - - -/** - * Convert a Base64 string to an ArrayBuffer. - * @param {string} b64Str - * @return An ArrayBuffer object. - */ - - -/** - * Convert a Base64 string to an ArrayBuffer. - * @param {string} b64Str - * @return An ArrayBuffer object. - */ - -// interface Base64 { -// toArrayBuffer: typeof toArrayBuffer, -// fromArrayBuffer: typeof fromArrayBuffer, -// } - -// export default Base64; -const REVERSE_LOOKUP: any = []; -REVERSE_LOOKUP['-'.charCodeAt(0)] = 62; -REVERSE_LOOKUP['_'.charCodeAt(0)] = 63; - -const BASE64_CODE: any = - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; -for (let i = 0; i < BASE64_CODE.length; i++) { - REVERSE_LOOKUP[BASE64_CODE.charCodeAt(i)] = i; -} - -// ---------------------------------------------------------------------------- -// Base64 analysis -// ---------------------------------------------------------------------------- - -function isValidChar(c: any) { - return REVERSE_LOOKUP[c.charCodeAt(0)] !== undefined; -} - -function extractChunks(b64Str: any) { - const strSize = b64Str.length; - const chunks = []; - - let currentChunk: any = null; - for (let i = 0; i < strSize; i++) { - if (isValidChar(b64Str[i])) { - if (!currentChunk) { - currentChunk = { start: i, count: 0 }; - } - currentChunk.count++; - currentChunk.end = i; - } else if (b64Str[i] === '=' && currentChunk) { - // End of chunk (found padding char) - chunks.push(currentChunk); - currentChunk = null; - } - } - - if (currentChunk) { - chunks.push(currentChunk); - } - return chunks; -} - -function writeChunk(b64Str: any, chunk: any, dstOffset: any, uint8: any) { - const { start, count } = chunk; - const remain = count % 4; - const fourCharProcessCount = Math.floor(count / 4); - let charIdx = start; - let tmp = null; - let offset = dstOffset; - - // Handle 4=>3 - for (let i = 0; i < fourCharProcessCount; i++) { - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp = REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] << 18; - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp |= REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] << 12; - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp |= REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] << 6; - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp |= REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)]; - - uint8[offset++] = (tmp >> 16) & 0xff; - uint8[offset++] = (tmp >> 8) & 0xff; - uint8[offset++] = tmp & 0xff; - } - - // Handle remain - switch (remain) { - case 3: - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp = REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] << 10; - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp |= REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] << 4; - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp |= REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] >> 2; - uint8[offset++] = (tmp >> 8) & 0xff; - uint8[offset++] = tmp & 0xff; - break; - case 2: - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp = REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] << 2; - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp |= REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] >> 4; - uint8[offset++] = tmp & 0xff; - break; - case 1: - throw new Error('BASE64: remain 1 should not happen'); - case 0: - break; - default: - break; - } - console.debug("offset", offset) - - return offset; -} - -export function toArrayBuffer(b64Str: any): ArrayBuffer { - console.debug(b64Str) - b64Str = b64Str["bvals"] - - const chunks = extractChunks(b64Str); - console.debug(chunks) - const totalEncodedLength = chunks[chunks.length - 1].end + 1; - const padding = (4 - (totalEncodedLength % 4)) % 4; // -length mod 4 - // Any padding chars in the middle of b64Str is to be interpreted as \x00, - // whereas the terminating padding chars are to be interpreted as literal padding. - const totalSize = ((totalEncodedLength + padding) * 3) / 4 - padding; - - const arrayBuffer = new ArrayBuffer(totalSize); - const view = new Uint8Array(arrayBuffer); - let dstOffset = 0; - for (let i = 0; i < chunks.length; i++) { - dstOffset += writeChunk(b64Str, chunks[i], dstOffset, view); - dstOffset += (4 - (chunks[i].count % 4)) % 4; - } - - return arrayBuffer; -} - -function encodeTriplet(v1: any, v2: any, v3: any) { - const triplet = (v1 << 16) + (v2 << 8) + v3; - return ( - BASE64_CODE[triplet >> 18] + - BASE64_CODE[(triplet >> 12) & 0x3f] + - BASE64_CODE[(triplet >> 6) & 0x3f] + - BASE64_CODE[triplet & 0x3f] - ); -} - -export function fromArrayBuffer(ab: any) { - const uint8 = new Uint8Array(ab); - const leftoverLength = ab.byteLength % 3; - const maxTripletIndex = ab.byteLength - leftoverLength; - const segments = Array(maxTripletIndex / 3); - for (let i = 0; i < segments.length; i++) { - const bufOffset = i * 3; - segments[i] = encodeTriplet( - uint8[bufOffset], - uint8[bufOffset + 1], - uint8[bufOffset + 2] - ); - } - if (leftoverLength > 0) { - const segment = encodeTriplet( - uint8[maxTripletIndex], - uint8[maxTripletIndex + 1] || 0, - uint8[maxTripletIndex + 2] || 0 - ); - if (leftoverLength === 1) { - segments.push(`${segment.substr(0, 2)}==`); - } else if (leftoverLength === 2) { - segments.push(`${segment.substr(0, 3)}=`); - } - } - return segments.join(''); -} - -export default { - toArrayBuffer, - fromArrayBuffer, -}; \ No newline at end of file diff --git a/frontend/src/modules/registerAllModules.ts b/frontend/src/modules/registerAllModules.ts index 7b692eb3a..dece6eeaf 100644 --- a/frontend/src/modules/registerAllModules.ts +++ b/frontend/src/modules/registerAllModules.ts @@ -3,7 +3,6 @@ import { isDevMode } from "@lib/utils/devMode"; import "./3DViewer/registerModule"; import "./DistributionPlot/registerModule"; import "./FlowNetwork/registerModule"; -// import "./Grid3DVTK/registerModule"; import "./InplaceVolumetrics/registerModule"; import "./Intersection/registerModule"; import "./Map/registerModule"; diff --git a/radixconfig.yml b/radixconfig.yml index 2600fba0c..ed7ef7a17 100644 --- a/radixconfig.yml +++ b/radixconfig.yml @@ -187,15 +187,6 @@ spec: payload: path: /compute/args - - name: user-grid3d-vtk - dockerFileName: ./backend_py/user_grid3d_vtk/Dockerfile - schedulerPort: 8003 - ports: - - name: http - port: 8003 - variables: - UVICORN_PORT: 8003 - dnsAppAlias: environment: prod component: frontend From 97dfa98efc269aae0339db3ebb659da00a6fa60f Mon Sep 17 00:00:00 2001 From: Sigurd Pettersen Date: Fri, 30 Aug 2024 10:03:14 +0200 Subject: [PATCH 6/7] Add support for explicit realization list when calculating surface statistics (#705) --- .../primary/primary/routers/surface/router.py | 4 +- .../services/sumo_access/surface_access.py | 47 ++++++++++++++----- .../_shared/Surface/SurfaceAddressBuilder.ts | 8 +++- 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/backend_py/primary/primary/routers/surface/router.py b/backend_py/primary/primary/routers/surface/router.py index 44d485578..02d74bab0 100644 --- a/backend_py/primary/primary/routers/surface/router.py +++ b/backend_py/primary/primary/routers/surface/router.py @@ -152,9 +152,6 @@ async def get_surface_data( raise HTTPException(status_code=404, detail="Could not get realization surface") elif addr.address_type == "STAT": - if addr.stat_realizations is not None: - raise HTTPException(status_code=501, detail="Statistics with specific realizations not yet supported") - service_stat_func_to_compute = StatisticFunction.from_string_value(addr.stat_function) if service_stat_func_to_compute is None: raise HTTPException(status_code=404, detail="Invalid statistic requested") @@ -164,6 +161,7 @@ async def get_surface_data( statistic_function=service_stat_func_to_compute, name=addr.name, attribute=addr.attribute, + realizations=addr.stat_realizations, time_or_interval_str=addr.iso_time_or_interval, ) perf_metrics.record_lap("sumo-calc") diff --git a/backend_py/primary/primary/services/sumo_access/surface_access.py b/backend_py/primary/primary/services/sumo_access/surface_access.py index 4c75df55e..f781aaffe 100644 --- a/backend_py/primary/primary/services/sumo_access/surface_access.py +++ b/backend_py/primary/primary/services/sumo_access/surface_access.py @@ -1,7 +1,7 @@ import asyncio import logging from io import BytesIO -from typing import Optional +from typing import Sequence import xtgeo @@ -118,10 +118,11 @@ async def get_observed_surfaces_metadata_async(self) -> SurfaceMetaSet: return surf_meta_set async def get_realization_surface_data_async( - self, real_num: int, name: str, attribute: str, time_or_interval_str: Optional[str] = None - ) -> Optional[xtgeo.RegularSurface]: + self, real_num: int, name: str, attribute: str, time_or_interval_str: str | None = None + ) -> xtgeo.RegularSurface | None: """ Get surface data for a realization surface + If time_or_interval_str is None, only surfaces with no time information will be considered. """ if not self._iteration_name: raise InvalidParameterError("Iteration name must be set to get realization surface", Service.SUMO) @@ -148,7 +149,7 @@ async def get_realization_surface_data_async( f"Multiple ({surf_count}) surfaces found in Sumo for: {surf_str}", Service.SUMO ) if surf_count == 0: - LOGGER.warning(f"No realization surface found in Sumo for {surf_str}") + LOGGER.warning(f"No realization surface found in Sumo for: {surf_str}") return None sumo_surf: Surface = await surface_collection.getitem_async(0) @@ -170,7 +171,7 @@ async def get_realization_surface_data_async( async def get_observed_surface_data_async( self, name: str, attribute: str, time_or_interval_str: str - ) -> Optional[xtgeo.RegularSurface]: + ) -> xtgeo.RegularSurface | None: """ Get surface data for an observed surface """ @@ -218,17 +219,25 @@ async def get_statistical_surface_data_async( statistic_function: StatisticFunction, name: str, attribute: str, - time_or_interval_str: Optional[str] = None, - ) -> Optional[xtgeo.RegularSurface]: + realizations: Sequence[int] | None = None, + time_or_interval_str: str | None = None, + ) -> xtgeo.RegularSurface | None: """ Compute statistic and return surface data + If realizations is None this is interpreted as a wildcard and surfaces from all realizations will be included + in the statistics. The list of realizations cannon be empty. + If time_or_interval_str is None, only surfaces with no time information will be considered. """ if not self._iteration_name: raise InvalidParameterError("Iteration name must be set to get realization surfaces", Service.SUMO) + if realizations is not None: + if len(realizations) == 0: + raise InvalidParameterError("List of realizations cannot be empty", Service.SUMO) + perf_metrics = PerfMetrics() - surf_str = self._make_real_surf_log_str(-1, name, attribute, time_or_interval_str) + surf_str = self._make_stat_surf_log_str(name, attribute, time_or_interval_str) time_filter = _time_or_interval_str_to_time_filter(time_or_interval_str) @@ -238,28 +247,38 @@ async def get_statistical_surface_data_async( iteration=self._iteration_name, name=name, tagname=attribute, + realization=realizations, time=time_filter, ) surf_count = await surface_collection.length_async() if surf_count == 0: - LOGGER.warning(f"No statistical source surfaces found in Sumo for {surf_str}") + LOGGER.warning(f"No statistical source surfaces found in Sumo for: {surf_str}") return None perf_metrics.record_lap("locate") - realizations = await surface_collection.realizations_async + realizations_found = await surface_collection.realizations_async perf_metrics.record_lap("collect-reals") + # Ensure that we got data for all the requested realizations + if realizations is not None: + missing_reals = list(set(realizations) - set(realizations_found)) + if len(missing_reals) > 0: + raise InvalidParameterError( + f"Could not find source surfaces for realizations: {missing_reals} in Sumo for {surf_str}", + Service.SUMO, + ) + xtgeo_surf = await _compute_statistical_surface_async(statistic_function, surface_collection) perf_metrics.record_lap("calc-stat") if not xtgeo_surf: - LOGGER.warning(f"Could not calculate statistical surface using Sumo for {surf_str}") + LOGGER.warning(f"Could not calculate statistical surface using Sumo for: {surf_str}") return None LOGGER.debug( f"Calculated statistical surface using Sumo in: {perf_metrics.to_string()} " - f"({surf_str} {len(realizations)=})" + f"[{xtgeo_surf.ncol}x{xtgeo_surf.nrow}, real count: {len(realizations_found)}] ({surf_str})" ) return xtgeo_surf @@ -272,6 +291,10 @@ def _make_obs_surf_log_str(self, name: str, attribute: str, date_str: str) -> st addr_str = f"N={name}, A={attribute}, D={date_str}, C={self._case_uuid}" return addr_str + def _make_stat_surf_log_str(self, name: str, attribute: str, date_str: str | None) -> str: + addr_str = f"N={name}, A={attribute}, D={date_str}, C={self._case_uuid}, I={self._iteration_name}" + return addr_str + def _build_surface_meta_arr( src_surf_info_arr: list[SurfInfo], time_type: SurfTimeType, are_observations: bool diff --git a/frontend/src/modules/_shared/Surface/SurfaceAddressBuilder.ts b/frontend/src/modules/_shared/Surface/SurfaceAddressBuilder.ts index 1f7b301fa..34ba220a6 100644 --- a/frontend/src/modules/_shared/Surface/SurfaceAddressBuilder.ts +++ b/frontend/src/modules/_shared/Surface/SurfaceAddressBuilder.ts @@ -15,6 +15,7 @@ export class SurfaceAddressBuilder { private _realizationNum: number | null = null; private _isoTimeOrInterval: string | null = null; private _statisticFunction: SurfaceStatisticFunction_api | null = null; + private _statisticRealizations: number[] | null = null; withType(addrType: SurfaceAddressType): this { this._addrType = addrType; @@ -52,6 +53,11 @@ export class SurfaceAddressBuilder { return this; } + withStatisticRealizations(realizations: number[]): this { + this._statisticRealizations = realizations; + return this; + } + buildRealizationAddress(): RealizationSurfaceAddress { if (this._addrType && this._addrType !== "REAL") { throw new Error("Address type is already set to another type than REAL"); @@ -114,7 +120,7 @@ export class SurfaceAddressBuilder { name: this._name!, attribute: this._attribute!, statFunction: this._statisticFunction, - statRealizations: null, + statRealizations: this._statisticRealizations, isoTimeOrInterval: this._isoTimeOrInterval, }; return retObj; From d59a6352822bd9ed7a1659f0bae32dc6854fcc39 Mon Sep 17 00:00:00 2001 From: Ruben Thoms <69145689+rubenthoms@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:47:03 +0200 Subject: [PATCH 7/7] Fix multi select initial anchor issue (#700) --- frontend/src/lib/components/Select/select.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/lib/components/Select/select.tsx b/frontend/src/lib/components/Select/select.tsx index 69f9905d9..b74f222de 100644 --- a/frontend/src/lib/components/Select/select.tsx +++ b/frontend/src/lib/components/Select/select.tsx @@ -68,7 +68,7 @@ export const Select = withDefaults()(defaultProps, (props) => { const [filteredOptions, setFilteredOptions] = React.useState(props.options); const [selectionAnchor, setSelectionAnchor] = React.useState(null); const [selectedOptionValues, setSelectedOptionValues] = React.useState([]); - const [prevPropsValue, setPrevPropsValue] = React.useState([]); + const [prevPropsValue, setPrevPropsValue] = React.useState(undefined); const [currentFocusIndex, setCurrentFocusIndex] = React.useState(0); const [virtualizationStartIndex, setVirtualizationStartIndex] = React.useState(0); const [reportedVirtualizationStartIndex, setReportedVirtualizationStartIndex] = React.useState(0); @@ -84,9 +84,10 @@ export const Select = withDefaults()(defaultProps, (props) => { filterOptions(newOptions, filterString); } - if (props.value && !isEqual(props.value, prevPropsValue)) { - setSelectedOptionValues([...props.value]); - setPrevPropsValue([...props.value]); + if (!isEqual(props.value, prevPropsValue)) { + setPrevPropsValue(props.value ? [...props.value] : undefined); + setSelectedOptionValues(props.value ? [...props.value] : []); + setSelectionAnchor(props.value ? filteredOptions.findIndex((option) => option.value === props.value[0]) : null); } const handleOnChange = React.useCallback( @@ -344,6 +345,7 @@ export const Select = withDefaults()(defaultProps, (props) => { setCurrentFocusIndex(newCurrentKeyboardFocusIndex); setVirtualizationStartIndex(newVirtualizationStartIndex); + setSelectionAnchor(newFilteredOptions.findIndex((option) => option.value === selectedOptionValues[0])); } function handleFilterChange(event: React.ChangeEvent) {