From c017009180343679da6e916447c52470c26901f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Lind-Johansen?= <47847084+lindjoha@users.noreply.github.com> Date: Sun, 11 Dec 2022 22:05:09 +0100 Subject: [PATCH] Changes to the `WellAnalysis` plugin (#1184) --- CHANGELOG.md | 2 + .../plugins/_well_analysis/_plugin.py | 17 +- .../_utils/_ensemble_well_analysis_data.py | 74 ++-- .../_well_overview_view/_settings/__init__.py | 4 + .../_settings/_chart_type.py | 30 ++ .../_well_overview_view/_settings/_filters.py | 29 ++ .../_settings/_layout_options.py | 75 ++++ .../_settings/_selections.py | 68 ++++ .../_utils/_well_overview_figure.py | 33 +- .../_views/_well_overview_view/_view.py | 348 ++++++------------ 10 files changed, 408 insertions(+), 272 deletions(-) create mode 100644 webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/__init__.py create mode 100644 webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_chart_type.py create mode 100644 webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_filters.py create mode 100644 webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_layout_options.py create mode 100644 webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_selections.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 82021a543..280588dd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#1182](https://github.com/equinor/webviz-subsurface/pull/1182) - `ParameterResponseCorrelation` can now allow default None column_keys when using arrow file as input - [#1122](https://github.com/equinor/webviz-subsurface/pull/1122) - `opm` and `ecl2df` are now optional, making `windows-subsurface` possible to install and import on non-unix based systems. **NOTE:** a lot of the functionality in `webviz-subsurface` is built on `opm` and `ecl2df`, and issues are therefore expected on eg Windows and macOS. Use with care. - [#1146](https://github.com/equinor/webviz-subsurface/pull/1146) - Converted the `BhpQc` plugin to WLF (Webviz Layout Framework). +- [#1184](https://github.com/equinor/webviz-subsurface/pull/1184) - `WellAnalysis`: changes to the settings layout in the `WellOverview` view ### Added - [#1162](https://github.com/equinor/webviz-subsurface/pull/1162) - `RelativePermeability` can now be initialized with only `pyscal` formatted input files instead of paths to ensembles using a new `scal_scenarios` input option. - [#1176](https://github.com/equinor/webviz-subsurface/pull/1176) - `MapViewerFMU` now has an option to download visualized surfaces. - [#1130](https://github.com/equinor/webviz-subsurface/pull/1130) - New plugin `VfpAnalysis` to vizualise and analyse VFP curves. +- [#1184](https://github.com/equinor/webviz-subsurface/pull/1184) - New option `Prod Until Date` in `WellAnalysis`. ### Fixed - [#1171](https://github.com/equinor/webviz-subsurface/pull/1171) - Fixed bug in `WellAnalysis` that caused an error if the selected date did not not exist in some selected ensembles. diff --git a/webviz_subsurface/plugins/_well_analysis/_plugin.py b/webviz_subsurface/plugins/_well_analysis/_plugin.py index 8564b320d..fa224889c 100644 --- a/webviz_subsurface/plugins/_well_analysis/_plugin.py +++ b/webviz_subsurface/plugins/_well_analysis/_plugin.py @@ -127,10 +127,21 @@ def tour_steps(self) -> List[dict]: }, { "id": self.view(self.Ids.WELL_OVERVIEW) - .settings_group(WellOverviewView.Ids.SETTINGS) + .settings_group(WellOverviewView.Ids.CHART_TYPE) .get_unique_id(), - "content": "Choose chart type, ensemble, type of production and other options. " - "You also change the layout of the chart.", + "content": "Choose chart type: bar, pie or area chart. ", + }, + { + "id": self.view(self.Ids.WELL_OVERVIEW) + .settings_group(WellOverviewView.Ids.SELECTIONS) + .get_unique_id(), + "content": "Choose ensembles, type of production and other options.", + }, + { + "id": self.view(self.Ids.WELL_OVERVIEW) + .settings_group(WellOverviewView.Ids.LAYOUT_OPTIONS) + .get_unique_id(), + "content": "Chart layout options.", }, { "id": self.view(self.Ids.WELL_OVERVIEW) diff --git a/webviz_subsurface/plugins/_well_analysis/_utils/_ensemble_well_analysis_data.py b/webviz_subsurface/plugins/_well_analysis/_utils/_ensemble_well_analysis_data.py index 30e956fff..e570b3aea 100644 --- a/webviz_subsurface/plugins/_well_analysis/_utils/_ensemble_well_analysis_data.py +++ b/webviz_subsurface/plugins/_well_analysis/_utils/_ensemble_well_analysis_data.py @@ -95,28 +95,45 @@ def filter_on_well_attributes( return filtered_wells def get_summary_data( - self, well_sumvec: str, prod_after_date: Union[datetime.datetime, None] + self, + well_sumvec: str, + prod_from_date: Union[datetime.datetime, None], + prod_until_date: Union[datetime.datetime, None], ) -> pd.DataFrame: - """Returns all summary data matching the well_sumvec. If the prod_after_date + """Returns all summary data matching the well_sumvec. If the prod_from_date is not None it will return all dates after that date and subtract the cumulative - production at that date. + production at that date. If prod_until_dates is not None it will filter out all + dates after that date. """ sumvecs = [f"{well_sumvec}:{well}" for well in self._wells] df = self._smry[["REAL", "DATE"] + sumvecs] + max_date = df["DATE"].max() + min_date = df["DATE"].min() + + if prod_from_date is not None: + df = df[df["DATE"] >= prod_from_date] + + # If the prod_from_date exists in the ensemble, subtract the + # production at that date from all dates. + if min_date <= prod_from_date <= max_date: + df_date = df[df["DATE"] == prod_from_date].copy() + df_merged = df.merge(df_date, on=["REAL"], how="inner") + for vec in sumvecs: + df_merged[vec] = df_merged[f"{vec}_x"] - df_merged[f"{vec}_y"] + df = df_merged[["REAL", "DATE_x"] + sumvecs].rename( + {"DATE_x": "DATE"}, axis=1 + ) + + if prod_until_date is not None: + df = df[df["DATE"] <= prod_until_date] - if prod_after_date is not None: - df = df[df["DATE"] >= prod_after_date] - df_date = df[df["DATE"] == df["DATE"].min()].copy() - df_merged = df.merge(df_date, on=["REAL"], how="inner") - for vec in sumvecs: - df_merged[vec] = df_merged[f"{vec}_x"] - df_merged[f"{vec}_y"] - df = df_merged[["REAL", "DATE_x"] + sumvecs].rename( - {"DATE_x": "DATE"}, axis=1 - ) return df def get_dataframe_melted( - self, well_sumvec: str, prod_after_date: Union[datetime.datetime, None] + self, + well_sumvec: str, + prod_from_date: Union[datetime.datetime, None], + prod_until_date: Union[datetime.datetime, None], ) -> pd.DataFrame: """Returns a dataframe on long form consisting of these columns: * WELL @@ -126,19 +143,26 @@ def get_dataframe_melted( sumvecs = [f"{well_sumvec}:{well}" for well in self._wells] df = self._smry[["REAL", "DATE"] + sumvecs] max_date = df["DATE"].max() - df = df[df["DATE"] == max_date] - - if prod_after_date is not None: - # Since the prod_after_dates can be chosen from the union of the date - # ranges from many ensembles, it can happen that the selecte date is - # > than the max_date for this ensemble. In that case we set it equal - # to the max date. The resulting sumvec value will then be zero for - # all wells. - prod_after_date = ( - max_date if prod_after_date > max_date else prod_after_date - ) + min_date = df["DATE"].min() + + if prod_until_date is None: + prod_until_date = max_date + else: + # Set prod_until_date to min_date or max_date if it is outside the + # ensemble date range + prod_until_date = max(min(prod_until_date, max_date), min_date) + + df = df[df["DATE"] == prod_until_date] + + # If prod_from_date is None, do nothing + if prod_from_date is not None: + # Set prod_from_date to min_date or max_date if it is outside the + # ensemble date range + prod_from_date = max(min(prod_from_date, max_date), min_date) + + # Subtract the production at the prod_from_date df_date = self._smry[["REAL", "DATE"] + sumvecs] - df_date = df_date[df_date["DATE"] >= prod_after_date] + df_date = df_date[df_date["DATE"] >= prod_from_date] df_date = df_date[df_date["DATE"] == df_date["DATE"].min()] df_merged = df.merge(df_date, on=["REAL"], how="inner") for vec in sumvecs: diff --git a/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/__init__.py b/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/__init__.py new file mode 100644 index 000000000..d3ef722ed --- /dev/null +++ b/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/__init__.py @@ -0,0 +1,4 @@ +from ._chart_type import WellOverviewChartType +from ._filters import WellOverviewFilters +from ._layout_options import WellOverviewLayoutOptions +from ._selections import WellOverviewSelections diff --git a/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_chart_type.py b/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_chart_type.py new file mode 100644 index 000000000..bd6f762af --- /dev/null +++ b/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_chart_type.py @@ -0,0 +1,30 @@ +from typing import List + +import webviz_core_components as wcc +from dash.development.base_component import Component +from webviz_config.utils import StrEnum +from webviz_config.webviz_plugin_subclasses import SettingsGroupABC + +from ...._types import ChartType + + +class WellOverviewChartType(SettingsGroupABC): + class Ids(StrEnum): + CHARTTYPE = "charttype" + + def __init__(self) -> None: + super().__init__("Chart Type") + + def layout(self) -> List[Component]: + return [ + wcc.RadioItems( + id=self.register_component_unique_id(self.Ids.CHARTTYPE), + options=[ + {"label": "Bar chart", "value": ChartType.BAR}, + {"label": "Pie chart", "value": ChartType.PIE}, + {"label": "Stacked area chart", "value": ChartType.AREA}, + ], + value=ChartType.BAR, + vertical=True, + ) + ] diff --git a/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_filters.py b/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_filters.py new file mode 100644 index 000000000..b3be83447 --- /dev/null +++ b/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_filters.py @@ -0,0 +1,29 @@ +from typing import List + +import webviz_core_components as wcc +from dash.development.base_component import Component +from webviz_config.utils import StrEnum +from webviz_config.webviz_plugin_subclasses import SettingsGroupABC + + +class WellOverviewFilters(SettingsGroupABC): + class Ids(StrEnum): + SELECTED_WELLS = "selected-wells" + + def __init__(self, wells: List[str]) -> None: + super().__init__("Filters") + self._wells = wells + + def layout(self) -> List[Component]: + return [ + wcc.SelectWithLabel( + label="Well", + size=min(10, len(self._wells)), + id=self.register_component_unique_id( + WellOverviewFilters.Ids.SELECTED_WELLS + ), + options=[{"label": well, "value": well} for well in self._wells], + value=self._wells, + multi=True, + ) + ] diff --git a/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_layout_options.py b/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_layout_options.py new file mode 100644 index 000000000..48db9745d --- /dev/null +++ b/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_layout_options.py @@ -0,0 +1,75 @@ +from typing import List + +import webviz_core_components as wcc +from dash import html +from dash.development.base_component import Component +from webviz_config.utils import StrEnum +from webviz_config.webviz_plugin_subclasses import SettingsGroupABC + +from ...._types import ChartType + + +class WellOverviewLayoutOptions(SettingsGroupABC): + class Ids(StrEnum): + CHARTTYPE_SETTINGS = "charttype-settings" + CHARTTYPE_CHECKLIST = "charttype-checklist" + + def __init__(self) -> None: + super().__init__("Layout Options") + + def layout(self) -> List[Component]: + settings_id = self.register_component_unique_id(self.Ids.CHARTTYPE_SETTINGS) + checklist_id = self.register_component_unique_id(self.Ids.CHARTTYPE_CHECKLIST) + return [ + html.Div( + children=[ + html.Div( + id={"id": settings_id, "charttype": ChartType.BAR}, + children=wcc.Checklist( + id={"id": checklist_id, "charttype": ChartType.BAR}, + options=[ + {"label": "Show legend", "value": "legend"}, + {"label": "Overlay bars", "value": "overlay_bars"}, + { + "label": "Show prod as text", + "value": "show_prod_text", + }, + { + "label": "White background", + "value": "white_background", + }, + ], + value=["legend"], + ), + ), + html.Div( + id={"id": settings_id, "charttype": ChartType.PIE}, + children=wcc.Checklist( + id={"id": checklist_id, "charttype": ChartType.PIE}, + options=[ + {"label": "Show legend", "value": "legend"}, + { + "label": "Show prod as text", + "value": "show_prod_text", + }, + ], + value=[], + ), + ), + html.Div( + id={"id": settings_id, "charttype": ChartType.AREA}, + children=wcc.Checklist( + id={"id": checklist_id, "charttype": ChartType.AREA}, + options=[ + {"label": "Show legend", "value": "legend"}, + { + "label": "White background", + "value": "white_background", + }, + ], + value=["legend"], + ), + ), + ], + ), + ] diff --git a/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_selections.py b/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_selections.py new file mode 100644 index 000000000..749c7cd97 --- /dev/null +++ b/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_settings/_selections.py @@ -0,0 +1,68 @@ +import datetime +from typing import List + +import webviz_core_components as wcc +from dash.development.base_component import Component +from webviz_config.utils import StrEnum +from webviz_config.webviz_plugin_subclasses import SettingsGroupABC + + +class WellOverviewSelections(SettingsGroupABC): + class Ids(StrEnum): + ENSEMBLES = "ensembles" + RESPONSE = "response" + PROD_FROM_DATE = "prod-from-date" + PROD_UNTIL_DATE = "prod-until-date" + + def __init__(self, ensembles: List[str], dates: List[datetime.datetime]) -> None: + super().__init__("Selections") + + self._ensembles = ensembles + self._dates = dates + + def layout(self) -> List[Component]: + return [ + wcc.Dropdown( + label="Ensembles", + id=self.register_component_unique_id(self.Ids.ENSEMBLES), + options=[{"label": col, "value": col} for col in self._ensembles], + value=self._ensembles, + multi=True, + ), + wcc.Dropdown( + label="Response", + id=self.register_component_unique_id(self.Ids.RESPONSE), + options=[ + {"label": "Oil production", "value": "WOPT"}, + {"label": "Gas production", "value": "WGPT"}, + {"label": "Water production", "value": "WWPT"}, + ], + value="WOPT", + multi=False, + clearable=False, + ), + wcc.Dropdown( + label="Production From Date", + id=self.register_component_unique_id(self.Ids.PROD_FROM_DATE), + options=[ + { + "label": dte.strftime("%Y-%m-%d"), + "value": dte.strftime("%Y-%m-%d"), + } + for dte in self._dates + ], + multi=False, + ), + wcc.Dropdown( + label="Production Until Date", + id=self.register_component_unique_id(self.Ids.PROD_UNTIL_DATE), + options=[ + { + "label": dte.strftime("%Y-%m-%d"), + "value": dte.strftime("%Y-%m-%d"), + } + for dte in self._dates + ], + multi=False, + ), + ] diff --git a/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_utils/_well_overview_figure.py b/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_utils/_well_overview_figure.py index bc4bd71a1..add63d24d 100644 --- a/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_utils/_well_overview_figure.py +++ b/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_utils/_well_overview_figure.py @@ -18,18 +18,20 @@ def __init__( ensembles: List[str], data_models: Dict[str, EnsembleWellAnalysisData], sumvec: str, - prod_after_date: Union[datetime.datetime, None], + prod_from_date: Union[datetime.datetime, None], + prod_until_date: Union[datetime.datetime, None], charttype: ChartType, - wells_selected: List[str], + wells: List[str], theme: WebvizConfigTheme, ) -> None: - + # pylint: disable=too-many-arguments self._ensembles = ensembles self._data_models = data_models self._sumvec = sumvec - self._prod_after_date = prod_after_date + self._prod_from_date = prod_from_date + self._prod_until_date = prod_until_date self._charttype = charttype - self._wells_selected = wells_selected + self._wells = wells self._colors = theme.plotly_theme["layout"]["colorway"] self._rows, self._cols = self.get_subplot_dim() spec_type = "scatter" if self._charttype == ChartType.AREA else self._charttype @@ -71,15 +73,19 @@ def _get_ensemble_charttype_data(self, ensemble: str) -> pd.DataFrame: """ if self._charttype in [ChartType.BAR, ChartType.PIE]: df = self._data_models[ensemble].get_dataframe_melted( - self._sumvec, self._prod_after_date + well_sumvec=self._sumvec, + prod_from_date=self._prod_from_date, + prod_until_date=self._prod_until_date, ) - df = df[df["WELL"].isin(self._wells_selected)] + df = df[df["WELL"].isin(self._wells)] df_mean = df.groupby("WELL").mean().reset_index() return df_mean[df_mean[self._sumvec] > 0] # else chart type == area df = self._data_models[ensemble].get_summary_data( - self._sumvec, self._prod_after_date + well_sumvec=self._sumvec, + prod_from_date=self._prod_from_date, + prod_until_date=self._prod_until_date, ) return df.groupby("DATE").mean().reset_index() @@ -125,7 +131,7 @@ def _add_traces(self) -> None: color_iterator = itertools.cycle(self._colors) for well in self._data_models[ensemble].wells: - if well in self._wells_selected: + if well in self._wells: showlegend = False if well not in wells_in_legend: showlegend = True @@ -154,7 +160,8 @@ def format_well_overview_figure( charttype: ChartType, settings: List[str], sumvec: str, - prod_after_date: Union[str, None], + prod_from_date: Union[str, None], + prod_until_date: Union[str, None], ) -> go.Figure: """This function formate the well overview figure. The reason for keeping this function outside the figure class is that we can update the figure formatting @@ -188,8 +195,10 @@ def format_well_overview_figure( # Make title phase = {"WOPT": "Oil", "WGPT": "Gas", "WWPT": "Water"}[sumvec] title = f"Cumulative Well {phase} Production (Sm3)" - if prod_after_date is not None: - title += f" after {prod_after_date}" + if prod_from_date is not None: + title += f" from {prod_from_date}" + if prod_until_date is not None: + title += f" until {prod_until_date}" figure.update( layout_title_text=title, diff --git a/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_view.py b/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_view.py index d2dc7b19d..bf3488c14 100644 --- a/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_view.py +++ b/webviz_subsurface/plugins/_well_analysis/_views/_well_overview_view/_view.py @@ -2,172 +2,92 @@ from typing import Any, Dict, List, Optional, Set, Union import plotly.graph_objects as go -import webviz_core_components as wcc -from dash import ALL, Input, Output, State, callback, callback_context, html +from dash import ALL, Input, Output, State, callback, callback_context from dash.development.base_component import Component from webviz_config import WebvizConfigTheme from webviz_config.utils import StrEnum, callback_typecheck -from webviz_config.webviz_plugin_subclasses import SettingsGroupABC, ViewABC +from webviz_config.webviz_plugin_subclasses import ViewABC from ..._types import ChartType from ..._utils import EnsembleWellAnalysisData +from ._settings import ( + WellOverviewChartType, + WellOverviewFilters, + WellOverviewLayoutOptions, + WellOverviewSelections, +) from ._utils import WellOverviewFigure, format_well_overview_figure from ._view_element import WellOverviewViewElement -class WellOverviewSettings(SettingsGroupABC): +class WellOverviewView(ViewABC): class Ids(StrEnum): - CHARTTYPE = "charttype" - ENSEMBLES = "ensembles" - RESPONSE = "response" - ONLY_PRODUCTION_AFTER_DATE = "only-production-after-date" - CHARTTYPE_SETTINGS = "charttype-settings" - CHARTTYPE_CHECKLIST = "charttype-checklist" + CHART_TYPE = "chart-type" + SELECTIONS = "selections" + LAYOUT_OPTIONS = "layout-options" + FILTERS = "filters" + CURRENT_FIGURE = "current-figure" + VIEW_ELEMENT = "view-element" - def __init__(self, data_models: Dict[str, EnsembleWellAnalysisData]) -> None: - super().__init__("Settings") + def __init__( + self, + data_models: Dict[str, EnsembleWellAnalysisData], + theme: WebvizConfigTheme, + ) -> None: + super().__init__("Well overview") - self._ensembles = list(data_models.keys()) + self._data_models = data_models + self._theme = theme + + ensembles = list(data_models.keys()) + # Make sorted list of dates in all ensembles dates: Set[datetime.datetime] = set() for _, ens_data_model in data_models.items(): dates = dates.union(ens_data_model.dates) - self._sorted_dates: List[datetime.datetime] = sorted(list(dates)) + sorted_dates: List[datetime.datetime] = sorted(list(dates)) - def layout(self) -> List[Component]: - settings_id = self.register_component_unique_id( - WellOverviewSettings.Ids.CHARTTYPE_SETTINGS - ) - checklist_id = self.register_component_unique_id( - WellOverviewSettings.Ids.CHARTTYPE_CHECKLIST + # Make list of all wells + wells: List[str] = [] + for ens_data_model in data_models.values(): + wells.extend([well for well in ens_data_model.wells if well not in wells]) + + self.add_settings_groups( + { + self.Ids.CHART_TYPE: WellOverviewChartType(), + self.Ids.SELECTIONS: WellOverviewSelections(ensembles, sorted_dates), + self.Ids.LAYOUT_OPTIONS: WellOverviewLayoutOptions(), + self.Ids.FILTERS: WellOverviewFilters(wells), + } ) - return [ - wcc.RadioItems( - id=self.register_component_unique_id( - WellOverviewSettings.Ids.CHARTTYPE - ), - label="Chart type", - options=[ - {"label": "Bar chart", "value": ChartType.BAR}, - {"label": "Pie chart", "value": ChartType.PIE}, - {"label": "Stacked area chart", "value": ChartType.AREA}, - ], - value=ChartType.BAR, - vertical=True, - ), - wcc.Dropdown( - label="Ensembles", - id=self.register_component_unique_id( - WellOverviewSettings.Ids.ENSEMBLES - ), - options=[{"label": col, "value": col} for col in self._ensembles], - value=self._ensembles, - multi=True, - ), - wcc.Dropdown( - label="Response", - id=self.register_component_unique_id(WellOverviewSettings.Ids.RESPONSE), - options=[ - {"label": "Oil production", "value": "WOPT"}, - {"label": "Gas production", "value": "WGPT"}, - {"label": "Water production", "value": "WWPT"}, - ], - value="WOPT", - multi=False, - clearable=False, - ), - wcc.Dropdown( - label="Only Production after date", - id=self.register_component_unique_id( - WellOverviewSettings.Ids.ONLY_PRODUCTION_AFTER_DATE - ), - options=[ - { - "label": dte.strftime("%Y-%m-%d"), - "value": dte.strftime("%Y-%m-%d"), - } - for dte in self._sorted_dates - ], - multi=False, - ), - html.Div( - children=[ - html.Div( - id={"id": settings_id, "charttype": ChartType.BAR}, - children=wcc.Checklist( - id={"id": checklist_id, "charttype": ChartType.BAR}, - label="Layout options", - options=[ - {"label": "Show legend", "value": "legend"}, - {"label": "Overlay bars", "value": "overlay_bars"}, - { - "label": "Show prod as text", - "value": "show_prod_text", - }, - { - "label": "White background", - "value": "white_background", - }, - ], - value=["legend"], - ), - ), - html.Div( - id={"id": settings_id, "charttype": ChartType.PIE}, - children=wcc.Checklist( - id={"id": checklist_id, "charttype": ChartType.PIE}, - label="Layout options", - options=[ - {"label": "Show legend", "value": "legend"}, - { - "label": "Show prod as text", - "value": "show_prod_text", - }, - ], - value=[], - ), - ), - html.Div( - id={"id": settings_id, "charttype": ChartType.AREA}, - children=wcc.Checklist( - id={"id": checklist_id, "charttype": ChartType.AREA}, - label="Layout options", - options=[ - {"label": "Show legend", "value": "legend"}, - { - "label": "White background", - "value": "white_background", - }, - ], - value=["legend"], - ), - ), - ], - ), - ] + + main_column = self.add_column() + main_column.add_view_element(WellOverviewViewElement(), self.Ids.VIEW_ELEMENT) def set_callbacks(self) -> None: @callback( Output( { - "id": self.component_unique_id( - WellOverviewSettings.Ids.CHARTTYPE_SETTINGS - ).to_string(), + "id": self.settings_group_unique_id( + self.Ids.LAYOUT_OPTIONS, + WellOverviewLayoutOptions.Ids.CHARTTYPE_SETTINGS, + ), "charttype": ALL, }, "style", ), Input( - self.component_unique_id( - WellOverviewSettings.Ids.CHARTTYPE - ).to_string(), + self.settings_group_unique_id( + self.Ids.CHART_TYPE, WellOverviewChartType.Ids.CHARTTYPE + ), "value", ), State( { - "id": self.component_unique_id( - WellOverviewSettings.Ids.CHARTTYPE_SETTINGS - ).to_string(), + "id": self.settings_group_unique_id( + self.Ids.LAYOUT_OPTIONS, + WellOverviewLayoutOptions.Ids.CHARTTYPE_SETTINGS, + ), "charttype": ALL, }, "id", @@ -185,61 +105,6 @@ def _display_charttype_settings( for settings_id in charttype_settings_ids ] - -class WellOverviewFilters(SettingsGroupABC): - class Ids(StrEnum): - SELECTED_WELLS = "selected-wells" - - def __init__(self, data_models: Dict[str, EnsembleWellAnalysisData]) -> None: - super().__init__("Filters") - self._wells: List[str] = [] - for ens_data_model in data_models.values(): - self._wells.extend( - [well for well in ens_data_model.wells if well not in self._wells] - ) - - def layout(self) -> List[Component]: - return [ - wcc.SelectWithLabel( - label="Well", - size=min(10, len(self._wells)), - id=self.register_component_unique_id( - WellOverviewFilters.Ids.SELECTED_WELLS - ), - options=[{"label": well, "value": well} for well in self._wells], - value=self._wells, - multi=True, - ) - ] - - -class WellOverviewView(ViewABC): - class Ids(StrEnum): - SETTINGS = "settings" - FILTERS = "filters" - CURRENT_FIGURE = "current-figure" - VIEW_ELEMENT = "view-element" - - def __init__( - self, - data_models: Dict[str, EnsembleWellAnalysisData], - theme: WebvizConfigTheme, - ) -> None: - super().__init__("Well overview") - - self._data_models = data_models - self._theme = theme - - self.add_settings_group( - WellOverviewSettings(self._data_models), self.Ids.SETTINGS - ) - self.add_settings_group( - WellOverviewFilters(self._data_models), self.Ids.FILTERS - ) - main_column = self.add_column() - main_column.add_view_element(WellOverviewViewElement(), self.Ids.VIEW_ELEMENT) - - def set_callbacks(self) -> None: @callback( Output( self.view_element(self.Ids.VIEW_ELEMENT) @@ -248,51 +113,59 @@ def set_callbacks(self) -> None: "figure", ), Input( - self.settings_group(self.Ids.SETTINGS) - .component_unique_id(WellOverviewSettings.Ids.ENSEMBLES) - .to_string(), + self.settings_group_unique_id( + self.Ids.SELECTIONS, WellOverviewSelections.Ids.ENSEMBLES + ), "value", ), Input( { - "id": self.settings_group(self.Ids.SETTINGS) - .component_unique_id(WellOverviewSettings.Ids.CHARTTYPE_CHECKLIST) - .to_string(), + "id": self.settings_group_unique_id( + self.Ids.LAYOUT_OPTIONS, + WellOverviewLayoutOptions.Ids.CHARTTYPE_CHECKLIST, + ), "charttype": ALL, }, "value", ), Input( - self.settings_group(self.Ids.SETTINGS) - .component_unique_id(WellOverviewSettings.Ids.RESPONSE) - .to_string(), + self.settings_group_unique_id( + self.Ids.SELECTIONS, WellOverviewSelections.Ids.RESPONSE + ), "value", ), Input( - self.settings_group(self.Ids.SETTINGS) - .component_unique_id( - WellOverviewSettings.Ids.ONLY_PRODUCTION_AFTER_DATE - ) - .to_string(), + self.settings_group_unique_id( + self.Ids.SELECTIONS, + WellOverviewSelections.Ids.PROD_FROM_DATE, + ), "value", ), Input( - self.settings_group(self.Ids.SETTINGS) - .component_unique_id(WellOverviewSettings.Ids.CHARTTYPE) - .to_string(), + self.settings_group_unique_id( + self.Ids.SELECTIONS, + WellOverviewSelections.Ids.PROD_UNTIL_DATE, + ), "value", ), Input( - self.settings_group(self.Ids.FILTERS) - .component_unique_id(WellOverviewFilters.Ids.SELECTED_WELLS) - .to_string(), + self.settings_group_unique_id( + self.Ids.CHART_TYPE, WellOverviewChartType.Ids.CHARTTYPE + ), + "value", + ), + Input( + self.settings_group_unique_id( + self.Ids.FILTERS, WellOverviewFilters.Ids.SELECTED_WELLS + ), "value", ), State( { - "id": self.settings_group(self.Ids.SETTINGS) - .component_unique_id(WellOverviewSettings.Ids.CHARTTYPE_CHECKLIST) - .to_string(), + "id": self.settings_group_unique_id( + self.Ids.LAYOUT_OPTIONS, + WellOverviewLayoutOptions.Ids.CHARTTYPE_CHECKLIST, + ), "charttype": ALL, }, "id", @@ -309,12 +182,14 @@ def _update_graph( ensembles: List[str], checklist_values: List[List[str]], sumvec: str, - prod_after_date: Union[str, None], + prod_from_date: Union[str, None], + prod_until_date: Union[str, None], charttype_selected: ChartType, wells_selected: List[str], checklist_ids: List[Dict[str, str]], current_fig_dict: Optional[Dict[str, Any]], ) -> Component: + # pylint: disable=too-many-arguments """Updates the well overview graph with selected input (f.ex chart type)""" ctx = callback_context.triggered[0]["prop_id"].split(".")[0] @@ -327,37 +202,46 @@ def _update_graph( # and not the figure data if ( current_fig_dict is not None - and self.settings_group(self.Ids.SETTINGS) - .component_unique_id(WellOverviewSettings.Ids.CHARTTYPE_CHECKLIST) + and self.settings_group(self.Ids.LAYOUT_OPTIONS) + .component_unique_id(WellOverviewLayoutOptions.Ids.CHARTTYPE_CHECKLIST) .to_string() in ctx ): fig_dict = format_well_overview_figure( - go.Figure(current_fig_dict), - charttype_selected, - settings[charttype_selected], - sumvec, - prod_after_date, + figure=go.Figure(current_fig_dict), + charttype=charttype_selected, + settings=settings[charttype_selected], + sumvec=sumvec, + prod_from_date=prod_from_date, + prod_until_date=prod_until_date, ) else: figure = WellOverviewFigure( - ensembles, - self._data_models, - sumvec, - datetime.datetime.strptime(prod_after_date, "%Y-%m-%d") - if prod_after_date is not None + ensembles=ensembles, + data_models=self._data_models, + sumvec=sumvec, + prod_from_date=datetime.datetime.strptime( + prod_from_date, "%Y-%m-%d" + ) + if prod_from_date is not None + else None, + prod_until_date=datetime.datetime.strptime( + prod_until_date, "%Y-%m-%d" + ) + if prod_until_date is not None else None, - charttype_selected, - wells_selected, - self._theme, + charttype=charttype_selected, + wells=wells_selected, + theme=self._theme, ) fig_dict = format_well_overview_figure( - figure.figure, - charttype_selected, - settings[charttype_selected], - sumvec, - prod_after_date, + figure=figure.figure, + charttype=charttype_selected, + settings=settings[charttype_selected], + sumvec=sumvec, + prod_from_date=prod_from_date, + prod_until_date=prod_until_date, ) return fig_dict