diff --git a/src/ert/gui/tools/plot/plot_api.py b/src/ert/gui/tools/plot/plot_api.py index 3e480f8bc56..0466b2fece6 100644 --- a/src/ert/gui/tools/plot/plot_api.py +++ b/src/ert/gui/tools/plot/plot_api.py @@ -205,29 +205,38 @@ def observations_for_key(self, ensemble_ids: List[str], key: str) -> pd.DataFram timeout=self._timeout, ) self._check_response(response) - if not response.json(): - continue + try: - obs = response.json()[0] + observations = response.json() + observations_dfs = [] + if not observations: + continue + + observations[0] # Just preserving the old logic/behavior + # but this should really be revised except (KeyError, IndexError, JSONDecodeError) as e: raise httpx.RequestError( f"Observation schema might have changed key={key}, ensemble_name={ensemble.name}, e={e}" ) from e - try: - int(obs["x_axis"][0]) - key_index = [int(v) for v in obs["x_axis"]] - except ValueError: - key_index = [pd.Timestamp(v) for v in obs["x_axis"]] - - data_struct = { - "STD": obs["errors"], - "OBS": obs["values"], - "key_index": key_index, - } - - all_observations = pd.concat( - [all_observations, pd.DataFrame(data_struct)] - ) + + for obs in observations: + try: + int(obs["x_axis"][0]) + key_index = [int(v) for v in obs["x_axis"]] + except ValueError: + key_index = [pd.Timestamp(v) for v in obs["x_axis"]] + + observations_dfs.append( + pd.DataFrame( + { + "STD": obs["errors"], + "OBS": obs["values"], + "key_index": key_index, + } + ) + ) + + all_observations = pd.concat([all_observations, *observations_dfs]) return all_observations.T diff --git a/tests/ert/unit_tests/gui/tools/plot/conftest.py b/tests/ert/unit_tests/gui/tools/plot/conftest.py index dc59088248b..faf827be512 100644 --- a/tests/ert/unit_tests/gui/tools/plot/conftest.py +++ b/tests/ert/unit_tests/gui/tools/plot/conftest.py @@ -90,48 +90,100 @@ def mocked_requests_get(*args, **kwargs): "/ensembles/ens_id_2": {"name": ".ensemble_2", "experiment_name": "experiment"}, "/ensembles/ens_id_3": {"name": "default_0", "experiment_name": "experiment"}, "/ensembles/ens_id_4": {"name": "default_1", "experiment_name": "experiment"}, - } - observations = { - "/ensembles/ens_id_3/records/WOPR:OP1/observations": { - "name": "WOPR:OP1", - "errors": [0.05, 0.07], - "values": [0.1, 0.7], - "x_axis": ["2010-03-31T00:00:00", "2010-12-26T00:00:00"], - }, - "/ensembles/ens_id_4/records/WOPR:OP1/observations": { - "name": "WOPR:OP1", - "errors": [0.05, 0.07], - "values": [0.1, 0.7], - "x_axis": ["2010-03-31T00:00:00", "2010-12-26T00:00:00"], - }, - "/ensembles/ens_id_3/records/SNAKE_OIL_WPR_DIFF@199/observations": { - "name": "SNAKE_OIL_WPR_DIFF", - "errors": [0.05, 0.07, 0.05], - "values": [0.1, 0.7, 0.5], - "x_axis": [ - "2010-03-31T00:00:00", - "2010-12-26T00:00:00", - "2011-12-21T00:00:00", - ], - }, - "/ensembles/ens_id_4/records/SNAKE_OIL_WPR_DIFF@199/observations": { - "name": "WOPR:OP1", - "errors": [0.05, 0.07, 0.05], - "values": [0.1, 0.7, 0.5], - "x_axis": [ - "2010-03-31T00:00:00", - "2010-12-26T00:00:00", - "2011-12-21T00:00:00", - ], - }, - "/ensembles/ens_id_3/records/FOPR/observations": { - "name": "FOPR", - "errors": [0.05, 0.07], - "values": [0.1, 0.7], - "x_axis": ["2010-03-31T00:00:00", "2010-12-26T00:00:00"], + "/ensembles/ens_id_5": { + "name": "default_manyobs", + "experiment_name": "experiment", }, } + observations = { + "/ensembles/ens_id_5/records/WOPR%25253AOP1/observations": [ + { + "name": "WOPR_OP1_108", + "errors": [0.07500000298023224], + "values": [0.30000001192092896], + "x_axis": ["2012-12-15T00:00:00"], + }, + { + "name": "WOPR_OP1_144", + "errors": [0.03500000014901161], + "values": [0.20000000298023224], + "x_axis": ["2013-12-10T00:00:00"], + }, + { + "name": "WOPR_OP1_190", + "errors": [0.009999999776482582], + "values": [0.014999999664723873], + "x_axis": ["2015-03-15T00:00:00"], + }, + { + "name": "WOPR_OP1_36", + "errors": [0.07000000029802322], + "values": [0.699999988079071], + "x_axis": ["2010-12-26T00:00:00"], + }, + { + "name": "WOPR_OP1_72", + "errors": [0.05000000074505806], + "values": [0.5], + "x_axis": ["2011-12-21T00:00:00"], + }, + { + "name": "WOPR_OP1_9", + "errors": [0.05000000074505806], + "values": [0.10000000149011612], + "x_axis": ["2010-03-31T00:00:00"], + }, + ], + "/ensembles/ens_id_3/records/WOPR:OP1/observations": [ + { + "name": "WOPR:OP1", + "errors": [0.05, 0.07], + "values": [0.1, 0.7], + "x_axis": ["2010-03-31T00:00:00", "2010-12-26T00:00:00"], + } + ], + "/ensembles/ens_id_4/records/WOPR:OP1/observations": [ + { + "name": "WOPR:OP1", + "errors": [0.05, 0.07], + "values": [0.1, 0.7], + "x_axis": ["2010-03-31T00:00:00", "2010-12-26T00:00:00"], + } + ], + "/ensembles/ens_id_3/records/SNAKE_OIL_WPR_DIFF@199/observations": [ + { + "name": "SNAKE_OIL_WPR_DIFF", + "errors": [0.05, 0.07, 0.05], + "values": [0.1, 0.7, 0.5], + "x_axis": [ + "2010-03-31T00:00:00", + "2010-12-26T00:00:00", + "2011-12-21T00:00:00", + ], + } + ], + "/ensembles/ens_id_4/records/SNAKE_OIL_WPR_DIFF@199/observations": [ + { + "name": "WOPR:OP1", + "errors": [0.05, 0.07, 0.05], + "values": [0.1, 0.7, 0.5], + "x_axis": [ + "2010-03-31T00:00:00", + "2010-12-26T00:00:00", + "2011-12-21T00:00:00", + ], + } + ], + "/ensembles/ens_id_3/records/FOPR/observations": [ + { + "name": "FOPR", + "errors": [0.05, 0.07], + "values": [0.1, 0.7], + "x_axis": ["2010-03-31T00:00:00", "2010-12-26T00:00:00"], + } + ], + } parameters = { "/ensembles/ens_id_1/parameters": [ { @@ -161,6 +213,14 @@ def mocked_requests_get(*args, **kwargs): "userdata": {"data_origin": "GEN_KW"}, }, ], + "/ensembles/ens_id_5/parameters": [ + { + "name": "I_AM_A_PARAM", + "dimensionality": 1, + "labels": [], + "userdata": {"data_origin": "GEN_KW"}, + }, + ], } responses = { @@ -198,12 +258,21 @@ def mocked_requests_get(*args, **kwargs): "has_observations": False, }, }, + "/ensembles/ens_id_5/responses": { + "WOPPER": { + "name": "WOPPER", + "id": "id_999", + "userdata": {"data_origin": "Summary"}, + "has_observations": False, + }, + }, } ensembles = { "/experiments/exp_1/ensembles": [ {"id": "ens_id_1", "userdata": {"name": "ensemble_1"}, "size": 25}, {"id": "ens_id_3", "userdata": {"name": "default_0"}, "size": 99}, + {"id": "ens_id_5", "userdata": {"name": "default_manyobs"}, "size": 25}, ] } @@ -220,7 +289,13 @@ def mocked_requests_get(*args, **kwargs): { "name": "default", "id": "exp_1", - "ensemble_ids": ["ens_id_1", "ens_id_2", "ens_id_3", "ens_id_4"], + "ensemble_ids": [ + "ens_id_1", + "ens_id_2", + "ens_id_3", + "ens_id_4", + "ens_id_5", + ], "priors": {}, "userdata": {}, } @@ -230,7 +305,7 @@ def mocked_requests_get(*args, **kwargs): return MockResponse({"userdata": ensemble[args[0]]}, 200) elif args[0] in observations: return MockResponse( - [observations[args[0]]], + observations[args[0]], 200, ) elif args[0] in ensembles: diff --git a/tests/ert/unit_tests/gui/tools/plot/test_plot_api.py b/tests/ert/unit_tests/gui/tools/plot/test_plot_api.py index ab1cb71e6b9..49ebfddd3a4 100644 --- a/tests/ert/unit_tests/gui/tools/plot/test_plot_api.py +++ b/tests/ert/unit_tests/gui/tools/plot/test_plot_api.py @@ -78,7 +78,13 @@ def test_case_structure(api): hidden_case = [ ensemble.name for ensemble in api.get_all_ensembles() if ensemble.hidden ] - expected = ["ensemble_1", ".ensemble_2", "default_0", "default_1"] + expected = [ + "ensemble_1", + ".ensemble_2", + "default_0", + "default_1", + "default_manyobs", + ] assert ensembles == expected assert hidden_case == [".ensemble_2"] @@ -314,3 +320,9 @@ def test_plot_api_handles_non_existant_gen_kw(api_and_storage): ) assert api.data_for_key(str(ensemble.id), "gen_kw").empty assert api.data_for_key(str(ensemble.id), "gen_kw:does_not_exist").empty + + +def test_that_multiple_observations_are_parsed_correctly(api): + ensemble = next(x for x in api.get_all_ensembles() if x.id == "ens_id_5") + obs_data = api.observations_for_key([ensemble.id], "WOPR:OP1") + assert obs_data.shape == (3, 6)