From b497e6d54d793f4143fae055feb3f29a7b081325 Mon Sep 17 00:00:00 2001 From: Frederik Kratzert Date: Wed, 15 Oct 2025 08:26:46 +0000 Subject: [PATCH 1/3] get_data now returns time indexed dataframes --- examples/test_australia_fetcher.py | 4 ++-- examples/test_canada_fetcher.py | 4 ++-- examples/test_chile_fetcher.py | 4 ++-- examples/test_france_fetcher.py | 6 +++--- examples/test_japan_fetcher.py | 4 ++-- examples/test_poland_fetcher.py | 4 ++-- examples/test_slovenia_fetcher.py | 4 ++-- examples/test_southafrica_fetcher.py | 4 ++-- examples/test_uk_fetcher.py | 2 +- examples/test_uk_nrfa_fetcher.py | 4 ++-- examples/test_usa_fetcher.py | 4 ++-- rivretrieve/australia.py | 2 +- rivretrieve/base.py | 6 ++++-- rivretrieve/canada.py | 2 +- rivretrieve/chile.py | 4 ++-- rivretrieve/france.py | 2 +- rivretrieve/japan.py | 6 +++--- rivretrieve/poland.py | 2 +- rivretrieve/slovenia.py | 4 ++-- rivretrieve/southafrica.py | 2 +- rivretrieve/uk.py | 4 ++-- rivretrieve/uk_nrfa.py | 4 ++-- rivretrieve/usa.py | 2 +- tests/test_australia.py | 2 +- tests/test_canada.py | 4 ++-- tests/test_chile.py | 2 +- tests/test_france.py | 2 +- tests/test_japan.py | 4 ++-- tests/test_poland.py | 8 ++++---- tests/test_slovenia.py | 8 ++++---- tests/test_uk.py | 4 ++-- tests/test_uk_nrfa.py | 4 ++-- tests/test_usa.py | 4 ++-- 33 files changed, 64 insertions(+), 62 deletions(-) diff --git a/examples/test_australia_fetcher.py b/examples/test_australia_fetcher.py index 122166d..2f5b424 100644 --- a/examples/test_australia_fetcher.py +++ b/examples/test_australia_fetcher.py @@ -23,9 +23,9 @@ if not data.empty: print(f"Data for {gauge_id}:") print(data.head()) - print(f"Time series from {data[constants.TIME_INDEX].min()} to {data[constants.TIME_INDEX].max()}") + print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( - data[constants.TIME_INDEX], + data.index, data[constants.DISCHARGE], label=gauge_id, marker="o", diff --git a/examples/test_canada_fetcher.py b/examples/test_canada_fetcher.py index 29abfc4..7e6b7b6 100644 --- a/examples/test_canada_fetcher.py +++ b/examples/test_canada_fetcher.py @@ -20,9 +20,9 @@ if not data.empty: print(f"Data for {gauge_id}:") print(data.head()) - print(f"Time series from {data[constants.TIME_INDEX].min()} to {data[constants.TIME_INDEX].max()}") + print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( - data[constants.TIME_INDEX], + data.index, data[constants.DISCHARGE], label=gauge_id, marker=".", diff --git a/examples/test_chile_fetcher.py b/examples/test_chile_fetcher.py index 4961d2a..9552375 100644 --- a/examples/test_chile_fetcher.py +++ b/examples/test_chile_fetcher.py @@ -20,9 +20,9 @@ if not data.empty: print(f"Data for {gauge_id}:") print(data.head()) - print(f"Time series from {data[constants.TIME_INDEX].min()} to {data[constants.TIME_INDEX].max()}") + print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( - data[constants.TIME_INDEX], + data.index, data[constants.DISCHARGE], label=gauge_id, marker=".", diff --git a/examples/test_france_fetcher.py b/examples/test_france_fetcher.py index e5fbb5d..da1983b 100644 --- a/examples/test_france_fetcher.py +++ b/examples/test_france_fetcher.py @@ -20,15 +20,15 @@ if not data.empty: print(f"Data for {gauge_id}:") print(data.head()) - print(f"Time series from {data[constants.TIME_INDEX].min()} to {data[constants.TIME_INDEX].max()}") + print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( - data[constants.TIME_INDEX], + data.index, data[constants.DISCHARGE], label=gauge_id, marker=".", linestyle="-", ) - plt.xlim(data[constants.TIME_INDEX].min(), data[constants.TIME_INDEX].max()) + plt.xlim(data.index.min(), data.index.max()) else: print(f"No data found for {gauge_id}") diff --git a/examples/test_japan_fetcher.py b/examples/test_japan_fetcher.py index a9c573b..ca565cc 100644 --- a/examples/test_japan_fetcher.py +++ b/examples/test_japan_fetcher.py @@ -22,9 +22,9 @@ if not data.empty: print(f"Data for {gauge_id}:") print(data.head()) - print(f"Time series from {data[constants.TIME_INDEX].min()} to {data[constants.TIME_INDEX].max()}") + print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( - data[constants.TIME_INDEX], + data.index, data[constants.DISCHARGE], label=gauge_id, marker="o", diff --git a/examples/test_poland_fetcher.py b/examples/test_poland_fetcher.py index 6df1e6b..782c726 100644 --- a/examples/test_poland_fetcher.py +++ b/examples/test_poland_fetcher.py @@ -29,9 +29,9 @@ if not data.empty: print(f"Data for {gauge_id}:") print(data.head()) - print(f"Time series from {data[constants.TIME_INDEX].min()} to {data[constants.TIME_INDEX].max()}") + print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( - data[constants.TIME_INDEX], + data.index, data[variable], label=gauge_id, marker=".", diff --git a/examples/test_slovenia_fetcher.py b/examples/test_slovenia_fetcher.py index 65ff03c..c3a822c 100644 --- a/examples/test_slovenia_fetcher.py +++ b/examples/test_slovenia_fetcher.py @@ -20,9 +20,9 @@ if not data.empty: print(f"Data for {gauge_id}:") print(data.head()) - print(f"Time series from {data[constants.TIME_INDEX].min()} to {data[constants.TIME_INDEX].max()}") + print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( - data[constants.TIME_INDEX], + data.index, data[constants.DISCHARGE], label=gauge_id, marker="o", diff --git a/examples/test_southafrica_fetcher.py b/examples/test_southafrica_fetcher.py index 23cb818..3a98f40 100644 --- a/examples/test_southafrica_fetcher.py +++ b/examples/test_southafrica_fetcher.py @@ -20,9 +20,9 @@ if not data.empty: print(f"Data for {gauge_id}:") print(data.head()) - print(f"Time series from {data[constants.TIME_INDEX].min()} to {data[constants.TIME_INDEX].max()}") + print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( - data[constants.TIME_INDEX], + data.index, data[constants.DISCHARGE], label=gauge_id, marker=".", diff --git a/examples/test_uk_fetcher.py b/examples/test_uk_fetcher.py index 2fef0a1..19031eb 100644 --- a/examples/test_uk_fetcher.py +++ b/examples/test_uk_fetcher.py @@ -19,7 +19,7 @@ print(f"Data for {gauge_id}:") print(data.head()) plt.plot( - data[constants.TIME_INDEX], + data.index, data[constants.DISCHARGE], label=gauge_id.split("/")[-1], ) diff --git a/examples/test_uk_nrfa_fetcher.py b/examples/test_uk_nrfa_fetcher.py index 14d76c9..54c6894 100644 --- a/examples/test_uk_nrfa_fetcher.py +++ b/examples/test_uk_nrfa_fetcher.py @@ -31,9 +31,9 @@ if not data.empty: print(f"Data for {gauge_id}:") print(data.head()) - print(f"Time series from {data[constants.TIME_INDEX].min()} to {data[constants.TIME_INDEX].max()}") + print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( - data[constants.TIME_INDEX], + data.index, data[constants.DISCHARGE], label=gauge_id, marker=".", diff --git a/examples/test_usa_fetcher.py b/examples/test_usa_fetcher.py index 4d12573..da6ba17 100644 --- a/examples/test_usa_fetcher.py +++ b/examples/test_usa_fetcher.py @@ -23,9 +23,9 @@ if not data.empty: print(f"Data for {gauge_id}:") print(data.head()) - print(f"Time series from {data[constants.TIME_INDEX].min()} to {data[constants.TIME_INDEX].max()}") + print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( - data[constants.TIME_INDEX], + data.index, data[constants.DISCHARGE], label=gauge_id, marker="o", diff --git a/rivretrieve/australia.py b/rivretrieve/australia.py index 01525ab..4a1b445 100644 --- a/rivretrieve/australia.py +++ b/rivretrieve/australia.py @@ -148,7 +148,7 @@ def _parse_data(self, gauge_id: str, raw_data: Optional[str], variable: str) -> df["Value"] = pd.to_numeric(df["Value"], errors="coerce") df = df.rename(columns={"Value": variable}) df[constants.TIME_INDEX] = pd.to_datetime(df[constants.TIME_INDEX]) - return df[[constants.TIME_INDEX, variable]].dropna() + return df[[constants.TIME_INDEX, variable]].dropna().set_index(constants.TIME_INDEX) except Exception as e: logger.error(f"Error parsing CSV data for site {gauge_id}: {e}") return pd.DataFrame(columns=[constants.TIME_INDEX, variable]) diff --git a/rivretrieve/base.py b/rivretrieve/base.py index 7ea8010..fd6746c 100644 --- a/rivretrieve/base.py +++ b/rivretrieve/base.py @@ -25,12 +25,14 @@ def get_data( Args: gauge_id: The site-specific identifier for the gauge. - variable: The variable to fetch (e.g., 'stage' or 'discharge'). + variable: The variable to fetch, should be one of the values from constants.py + (e.g., constants.DISCHARGE, constants.STAGE). start_date: Optional start date in 'YYYY-MM-DD' format. end_date: Optional end date in 'YYYY-MM-DD' format. Returns: - A pandas DataFrame with 'Date' and the variable column ('H' or 'Q'). + A pandas DataFrame indexed by time (constants.TIME_INDEX) with a column + for the requested variable (e.g., constants.DISCHARGE). """ pass diff --git a/rivretrieve/canada.py b/rivretrieve/canada.py index 9d788a5..589c414 100644 --- a/rivretrieve/canada.py +++ b/rivretrieve/canada.py @@ -186,7 +186,7 @@ def get_data( df_long[[constants.TIME_INDEX, variable]] .dropna() .sort_values(by=constants.TIME_INDEX) - .reset_index(drop=True) + .set_index(constants.TIME_INDEX) ) except Exception as e: diff --git a/rivretrieve/chile.py b/rivretrieve/chile.py index 930df5e..0ad25de 100644 --- a/rivretrieve/chile.py +++ b/rivretrieve/chile.py @@ -104,7 +104,7 @@ def _parse_data( df[[constants.TIME_INDEX, variable]] .dropna() .sort_values(by=constants.TIME_INDEX) - .reset_index(drop=True) + .set_index(constants.TIME_INDEX) ) except Exception as e: logger.error(f"Error parsing data for site {gauge_id}: {e}") @@ -135,7 +135,7 @@ def get_data( # Filter by date range start_date_dt = pd.to_datetime(start_date) end_date_dt = pd.to_datetime(end_date) - df = df[(df[constants.TIME_INDEX] >= start_date_dt) & (df[constants.TIME_INDEX] <= end_date_dt)] + df = df[(df.index >= start_date_dt) & (df.index <= end_date_dt)] return df except Exception as e: logger.error(f"Failed to get data for site {gauge_id}, variable {variable}: {e}") diff --git a/rivretrieve/france.py b/rivretrieve/france.py index 96972b9..64791fa 100644 --- a/rivretrieve/france.py +++ b/rivretrieve/france.py @@ -107,7 +107,7 @@ def _parse_data( df[[constants.TIME_INDEX, variable]] .dropna() .sort_values(by=constants.TIME_INDEX) - .reset_index(drop=True) + .set_index(constants.TIME_INDEX) ) except Exception as e: logger.error(f"Error parsing JSON data for site {gauge_id}: {e}") diff --git a/rivretrieve/japan.py b/rivretrieve/japan.py index 8817dcb..eb818c2 100644 --- a/rivretrieve/japan.py +++ b/rivretrieve/japan.py @@ -141,9 +141,9 @@ def _parse_data( final_df = pd.concat(all_dfs, ignore_index=True) final_df = final_df.rename(columns={"Value": variable}) - final_df = final_df.sort_values(by=constants.TIME_INDEX).reset_index(drop=True) + final_df = final_df.sort_values(by=constants.TIME_INDEX) - return final_df + return final_df.set_index(constants.TIME_INDEX) def get_data( self, @@ -164,7 +164,7 @@ def get_data( start_date_dt = pd.to_datetime(start_date) end_date_dt = pd.to_datetime(end_date) - df = df[(df[constants.TIME_INDEX] >= start_date_dt) & (df[constants.TIME_INDEX] <= end_date_dt)] + df = df[(df.index >= start_date_dt) & (df.index <= end_date_dt)] return df except Exception as e: diff --git a/rivretrieve/poland.py b/rivretrieve/poland.py index 1f46116..f8619e8 100644 --- a/rivretrieve/poland.py +++ b/rivretrieve/poland.py @@ -209,7 +209,7 @@ def get_data( data_array = ds[variable].sel(gauge_id=gauge_id, time=slice(start_date, end_date)) df = data_array.to_pandas().dropna().reset_index().rename(columns={variable: variable}) - return df[[constants.TIME_INDEX, variable]] + return df.set_index(constants.TIME_INDEX)[[variable]] except KeyError: logger.info(f"No data found for gauge {gauge_id} in the selected date range.") diff --git a/rivretrieve/slovenia.py b/rivretrieve/slovenia.py index 72f8159..4fb0f44 100644 --- a/rivretrieve/slovenia.py +++ b/rivretrieve/slovenia.py @@ -85,7 +85,7 @@ def _parse_data(self, gauge_id: str, raw_data: Optional[str], variable: str) -> df[[constants.TIME_INDEX, variable]] .dropna() .sort_values(by=constants.TIME_INDEX) - .reset_index(drop=True) + .set_index(constants.TIME_INDEX) ) except Exception as e: @@ -113,7 +113,7 @@ def get_data( # Filter by date range start_date_dt = pd.to_datetime(start_date) end_date_dt = pd.to_datetime(end_date) - df = df[(df[constants.TIME_INDEX] >= start_date_dt) & (df[constants.TIME_INDEX] <= end_date_dt)] + df = df[(df.index >= start_date_dt) & (df.index <= end_date_dt)] return df except Exception as e: logger.error(f"Failed to get data for site {gauge_id}, variable {variable}: {e}") diff --git a/rivretrieve/southafrica.py b/rivretrieve/southafrica.py index 6d403b6..450e8ac 100644 --- a/rivretrieve/southafrica.py +++ b/rivretrieve/southafrica.py @@ -155,7 +155,7 @@ def _parse_data( daily_df = full_df[[constants.TIME_INDEX, "D_AVG_FR"]].rename(columns={"D_AVG_FR": "Value"}) daily_df = daily_df.rename(columns={"Value": variable}) - return daily_df.dropna().sort_values(by=constants.TIME_INDEX).reset_index(drop=True) + return daily_df.dropna().sort_values(by=constants.TIME_INDEX).set_index(constants.TIME_INDEX) except Exception as e: logger.error(f"Error parsing data for site {gauge_id}: {e}") diff --git a/rivretrieve/uk.py b/rivretrieve/uk.py index 3754275..237315d 100644 --- a/rivretrieve/uk.py +++ b/rivretrieve/uk.py @@ -126,7 +126,7 @@ def _parse_data(self, gauge_id: str, raw_data: List[Dict[str, Any]], variable: s complete_ts = pd.DataFrame(date_range, columns=[constants.TIME_INDEX]) df_daily = pd.merge(complete_ts, df_daily, on=constants.TIME_INDEX, how="left") - return df_daily + return df_daily.set_index(constants.TIME_INDEX) def get_data( self, @@ -148,7 +148,7 @@ def get_data( # Filter by exact start and end date after processing start_date_dt = pd.to_datetime(start_date) end_date_dt = pd.to_datetime(end_date) - df = df[(df[constants.TIME_INDEX] >= start_date_dt) & (df[constants.TIME_INDEX] <= end_date_dt)] + df = df[(df.index >= start_date_dt) & (df.index <= end_date_dt)] return df except Exception as e: diff --git a/rivretrieve/uk_nrfa.py b/rivretrieve/uk_nrfa.py index 07186c5..18747a4 100644 --- a/rivretrieve/uk_nrfa.py +++ b/rivretrieve/uk_nrfa.py @@ -102,7 +102,7 @@ def _parse_data(self, gauge_id: str, raw_data: Optional[Dict[str, Any]], variabl df[constants.TIME_INDEX] = pd.to_datetime(df["time"], format="ISO8601").dt.date df[constants.TIME_INDEX] = pd.to_datetime(df[constants.TIME_INDEX]) df[variable] = pd.to_numeric(df[variable], errors="coerce") - return df[[constants.TIME_INDEX, variable]].dropna().reset_index(drop=True) + return df[[constants.TIME_INDEX, variable]].dropna().set_index(constants.TIME_INDEX) except Exception as e: logger.error(f"Error parsing NRFA data for {gauge_id}: {e}") return pd.DataFrame(columns=[constants.TIME_INDEX, variable]) @@ -128,7 +128,7 @@ def get_data( # Filter by date range start_date_dt = pd.to_datetime(start_date) end_date_dt = pd.to_datetime(end_date) - df = df[(df[constants.TIME_INDEX] >= start_date_dt) & (df[constants.TIME_INDEX] <= end_date_dt)] + df = df[(df.index >= start_date_dt) & (df.index <= end_date_dt)] return df except Exception as e: logger.error(f"Failed to get data for site {gauge_id}, variable {variable}: {e}") diff --git a/rivretrieve/usa.py b/rivretrieve/usa.py index c689fc1..0ded443 100644 --- a/rivretrieve/usa.py +++ b/rivretrieve/usa.py @@ -76,7 +76,7 @@ def _parse_data(self, gauge_id: str, raw_data: pd.DataFrame, variable: str) -> p mult = 0.0283168466 df[variable] = pd.to_numeric(df[value_col], errors="coerce") * mult - return df[[constants.TIME_INDEX, variable]].dropna() + return df[[constants.TIME_INDEX, variable]].dropna().set_index(constants.TIME_INDEX) def get_data( self, diff --git a/tests/test_australia.py b/tests/test_australia.py index a81c9d6..9f63588 100644 --- a/tests/test_australia.py +++ b/tests/test_australia.py @@ -47,7 +47,7 @@ def bom_request_side_effect(params): constants.TIME_INDEX: pd.to_datetime(["2010-01-01", "2010-01-02", "2010-01-03"]), constants.DISCHARGE: [0.000, 3.710, 3.211], } - expected_df = pd.DataFrame(expected_data) + expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) assert_frame_equal(result_df, expected_df) self.assertEqual(mock_make_bom_request.call_count, 2) diff --git a/tests/test_canada.py b/tests/test_canada.py index 6771741..85c2079 100644 --- a/tests/test_canada.py +++ b/tests/test_canada.py @@ -36,7 +36,7 @@ def test_get_data_discharge(self, mock_hydat_path, mock_download, mock_requests) ), constants.DISCHARGE: [1.1, 1.2, 1.3, 1.4, 1.5], } - expected_df = pd.DataFrame(expected_data) + expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) assert_frame_equal(result_df, expected_df) @@ -62,7 +62,7 @@ def test_get_data_stage(self, mock_hydat_path, mock_download, mock_requests): ), constants.STAGE: [10.1, 10.2, 10.3, 10.4, 10.5], } - expected_df = pd.DataFrame(expected_data) + expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) assert_frame_equal(result_df, expected_df) diff --git a/tests/test_chile.py b/tests/test_chile.py index 0f35af0..cd86075 100644 --- a/tests/test_chile.py +++ b/tests/test_chile.py @@ -50,7 +50,7 @@ def get_side_effect(*args, **kwargs): constants.TIME_INDEX: pd.to_datetime(["2022-01-01", "2022-01-02", "2022-01-03"]), constants.DISCHARGE: [15.5, 16.0, 15.8], } - expected_df = pd.DataFrame(expected_data) + expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) assert_frame_equal(result_df, expected_df) self.assertEqual(mock_get.call_count, 2) diff --git a/tests/test_france.py b/tests/test_france.py index 9d056d7..6d5701e 100644 --- a/tests/test_france.py +++ b/tests/test_france.py @@ -38,7 +38,7 @@ def test_get_data_discharge(self, mock_get): constants.TIME_INDEX: pd.to_datetime(["2023-01-01", "2023-01-02", "2023-01-03"]), constants.DISCHARGE: [15.0005, 16.0000, 15.5002], # Divided by 1000 } - expected_df = pd.DataFrame(expected_data) + expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) assert_frame_equal(result_df, expected_df) mock_get.assert_called_once() diff --git a/tests/test_japan.py b/tests/test_japan.py index 7501af3..423c65b 100644 --- a/tests/test_japan.py +++ b/tests/test_japan.py @@ -40,9 +40,9 @@ def test_get_data_discharge(self, mock_get): constants.TIME_INDEX: expected_dates, constants.DISCHARGE: expected_values, } - expected_df = pd.DataFrame(expected_data) + expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) - assert_frame_equal(result_df.reset_index(drop=True), expected_df) + assert_frame_equal(result_df, expected_df) mock_get.assert_called_once() # Check that the params are correct mock_args, mock_kwargs = mock_get.call_args diff --git a/tests/test_poland.py b/tests/test_poland.py index 489f564..22e1681 100644 --- a/tests/test_poland.py +++ b/tests/test_poland.py @@ -46,9 +46,9 @@ def test_get_data_discharge(self, mock_create_cache): constants.TIME_INDEX: expected_dates, constants.DISCHARGE: expected_values, } - expected_df = pd.DataFrame(expected_data) + expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) - assert_frame_equal(result_df.reset_index(drop=True), expected_df) + assert_frame_equal(result_df, expected_df) mock_create_cache.assert_not_called() # Cache should not be recreated @patch("rivretrieve.poland.PolandFetcher._create_cache") # Mock cache creation @@ -67,9 +67,9 @@ def test_get_data_stage(self, mock_create_cache): constants.TIME_INDEX: expected_dates, constants.STAGE: expected_values, } - expected_df = pd.DataFrame(expected_data) + expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) - assert_frame_equal(result_df.reset_index(drop=True), expected_df) + assert_frame_equal(result_df, expected_df) mock_create_cache.assert_not_called() @patch("rivretrieve.utils.requests_retry_session") diff --git a/tests/test_slovenia.py b/tests/test_slovenia.py index 1c66615..c254f29 100644 --- a/tests/test_slovenia.py +++ b/tests/test_slovenia.py @@ -45,9 +45,9 @@ def test_get_data_discharge(self, mock_requests_session): constants.TIME_INDEX: expected_dates, constants.DISCHARGE: expected_values, } - expected_df = pd.DataFrame(expected_data) + expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) - assert_frame_equal(result_df.reset_index(drop=True), expected_df) + assert_frame_equal(result_df, expected_df) mock_session.get.assert_called_once() mock_args, mock_kwargs = mock_session.get.call_args self.assertIn("p_postaja=1020", mock_args[0]) @@ -81,9 +81,9 @@ def test_get_data_stage(self, mock_requests_session): constants.TIME_INDEX: expected_dates, constants.STAGE: expected_values, } - expected_df = pd.DataFrame(expected_data) + expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) - assert_frame_equal(result_df.reset_index(drop=True), expected_df) + assert_frame_equal(result_df, expected_df) if __name__ == "__main__": diff --git a/tests/test_uk.py b/tests/test_uk.py index ec40aeb..ab980cd 100644 --- a/tests/test_uk.py +++ b/tests/test_uk.py @@ -61,9 +61,9 @@ def mock_get_side_effect(url, *args, **kwargs): constants.TIME_INDEX: expected_dates, constants.DISCHARGE: expected_values, } - expected_df = pd.DataFrame(expected_data) + expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) - assert_frame_equal(result_df.reset_index(drop=True), expected_df, check_dtype=False) + assert_frame_equal(result_df, expected_df, check_dtype=False) self.assertEqual(mock_session.get.call_count, 2) diff --git a/tests/test_uk_nrfa.py b/tests/test_uk_nrfa.py index 6e5aa08..56bc1d9 100644 --- a/tests/test_uk_nrfa.py +++ b/tests/test_uk_nrfa.py @@ -99,9 +99,9 @@ def test_get_data(self, variable, expected_data_type, sample_file, expected_valu expected_dates = pd.to_datetime(["2022-01-01", "2022-01-02", "2022-01-03", "2022-01-04", "2022-01-05"]) expected_data = {constants.TIME_INDEX: expected_dates, variable: expected_values} - expected_df = pd.DataFrame(expected_data) + expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) - assert_frame_equal(result_df.reset_index(drop=True), expected_df, check_dtype=False) + assert_frame_equal(result_df, expected_df, check_dtype=False) mock_session.get.assert_called_once() _, mock_kwargs = mock_session.get.call_args params = mock_kwargs["params"] diff --git a/tests/test_usa.py b/tests/test_usa.py index 09792d2..e7a16a2 100644 --- a/tests/test_usa.py +++ b/tests/test_usa.py @@ -51,9 +51,9 @@ def test_get_data_discharge(self, mock_get_dv): constants.TIME_INDEX: expected_dates, constants.DISCHARGE: expected_values, } - expected_df = pd.DataFrame(expected_data) + expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) - assert_frame_equal(result_df.reset_index(drop=True), expected_df, check_dtype=False) + assert_frame_equal(result_df, expected_df, check_dtype=False) mock_get_dv.assert_called_once() mock_args, mock_kwargs = mock_get_dv.call_args self.assertEqual(mock_kwargs["sites"], gauge_id) From 31db000981a06414caf4baf943810556d4ef69ab Mon Sep 17 00:00:00 2001 From: Frederik Kratzert Date: Thu, 16 Oct 2025 20:30:45 +0000 Subject: [PATCH 2/3] Refactor: New names, Extended USA and South Africa, Fixed France --- examples/test_australia_fetcher.py | 6 +-- examples/test_canada_fetcher.py | 6 +-- examples/test_chile_fetcher.py | 6 +-- examples/test_france_fetcher.py | 6 +-- examples/test_japan_fetcher.py | 6 +-- examples/test_poland_fetcher.py | 2 +- examples/test_slovenia_fetcher.py | 6 +-- examples/test_southafrica_fetcher.py | 8 ++-- examples/test_uk_ea_fetcher.py | 6 +-- examples/test_uk_nrfa_fetcher.py | 6 +-- examples/test_usa_fetcher.py | 6 +-- rivretrieve/australia.py | 6 +-- rivretrieve/canada.py | 6 +-- rivretrieve/chile.py | 11 ++--- rivretrieve/constants.py | 50 ++++++++++++++++---- rivretrieve/france.py | 31 ++++++++----- rivretrieve/japan.py | 6 +-- rivretrieve/poland.py | 18 ++++---- rivretrieve/slovenia.py | 6 +-- rivretrieve/southafrica.py | 44 ++++++++++-------- rivretrieve/uk_ea.py | 36 ++++++--------- rivretrieve/uk_nrfa.py | 6 +-- rivretrieve/usa.py | 66 +++++++++++++++++++-------- tests/test_australia.py | 4 +- tests/test_canada.py | 8 ++-- tests/test_chile.py | 4 +- tests/test_data/poland_test.zarr.zip | Bin 6447 -> 6955 bytes tests/test_france.py | 4 +- tests/test_japan.py | 4 +- tests/test_poland.py | 14 +++--- tests/test_slovenia.py | 8 ++-- tests/test_southafrica.py | 4 +- tests/test_uk_ea.py | 4 +- tests/test_uk_nrfa.py | 4 +- tests/test_usa.py | 4 +- 35 files changed, 238 insertions(+), 174 deletions(-) diff --git a/examples/test_australia_fetcher.py b/examples/test_australia_fetcher.py index 2f5b424..22631cc 100644 --- a/examples/test_australia_fetcher.py +++ b/examples/test_australia_fetcher.py @@ -9,7 +9,7 @@ gauge_ids = [ "403213", ] -variable = constants.DISCHARGE +variable = constants.DISCHARGE_DAILY_MEAN # Fetch a recent period for testing start_date = "2023-10-01" end_date = "2024-03-31" @@ -26,7 +26,7 @@ print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( data.index, - data[constants.DISCHARGE], + data[constants.DISCHARGE_DAILY_MEAN], label=gauge_id, marker="o", ) @@ -34,7 +34,7 @@ print(f"No data found for {gauge_id}") plt.xlabel(constants.TIME_INDEX) -plt.ylabel(f"{constants.DISCHARGE} (m3/s)") +plt.ylabel(f"{constants.DISCHARGE_DAILY_MEAN} (m3/s)") plt.title(f"Australia River Discharge ({gauge_ids[0]} - Full Time Series)") plt.legend() plt.grid(True) diff --git a/examples/test_canada_fetcher.py b/examples/test_canada_fetcher.py index 7e6b7b6..52f58f4 100644 --- a/examples/test_canada_fetcher.py +++ b/examples/test_canada_fetcher.py @@ -9,7 +9,7 @@ gauge_ids = [ "01AD003", ] -variable = constants.DISCHARGE +variable = constants.DISCHARGE_DAILY_MEAN plt.figure(figsize=(12, 6)) @@ -23,7 +23,7 @@ print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( data.index, - data[constants.DISCHARGE], + data[constants.DISCHARGE_DAILY_MEAN], label=gauge_id, marker=".", linestyle="-", @@ -33,7 +33,7 @@ if "data" in locals() and not data.empty: plt.xlabel(constants.TIME_INDEX) - plt.ylabel(f"{constants.DISCHARGE} (m3/s)") + plt.ylabel(f"{constants.DISCHARGE_DAILY_MEAN} (m3/s)") plt.title(f"Canada River Discharge ({gauge_ids[0]} - Full Time Series)") plt.legend() plt.grid(True) diff --git a/examples/test_chile_fetcher.py b/examples/test_chile_fetcher.py index 9552375..37dd496 100644 --- a/examples/test_chile_fetcher.py +++ b/examples/test_chile_fetcher.py @@ -9,7 +9,7 @@ gauge_ids = [ "01201005", ] -variable = constants.DISCHARGE +variable = constants.DISCHARGE_DAILY_MEAN plt.figure(figsize=(12, 6)) @@ -23,7 +23,7 @@ print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( data.index, - data[constants.DISCHARGE], + data[constants.DISCHARGE_DAILY_MEAN], label=gauge_id, marker=".", linestyle="-", @@ -33,7 +33,7 @@ if "data" in locals() and not data.empty: plt.xlabel(constants.TIME_INDEX) - plt.ylabel(f"{constants.DISCHARGE} (m3/s)") + plt.ylabel(f"{constants.DISCHARGE_DAILY_MEAN} (m3/s)") plt.title(f"Chile River Discharge ({gauge_ids[0]} - Full Time Series)") plt.legend() plt.grid(True) diff --git a/examples/test_france_fetcher.py b/examples/test_france_fetcher.py index da1983b..3cbebdc 100644 --- a/examples/test_france_fetcher.py +++ b/examples/test_france_fetcher.py @@ -9,7 +9,7 @@ gauge_ids = [ "K027401001", ] -variable = constants.DISCHARGE +variable = constants.STAGE_DAILY_MAX plt.figure(figsize=(12, 6)) @@ -23,7 +23,7 @@ print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( data.index, - data[constants.DISCHARGE], + data[variable], label=gauge_id, marker=".", linestyle="-", @@ -34,7 +34,7 @@ if not data.empty: plt.xlabel(constants.TIME_INDEX) - plt.ylabel(f"{constants.DISCHARGE} (m3/s)") + plt.ylabel(f"{variable} (m3/s)") plt.title(f"France River Discharge ({gauge_ids[0]} - Full Time Series)") plt.legend() plt.grid(True) diff --git a/examples/test_japan_fetcher.py b/examples/test_japan_fetcher.py index ca565cc..18ceed1 100644 --- a/examples/test_japan_fetcher.py +++ b/examples/test_japan_fetcher.py @@ -9,7 +9,7 @@ gauge_ids = [ "301011281104010", ] -variable = constants.DISCHARGE +variable = constants.DISCHARGE_DAILY_MEAN start_date = "2019-01-01" end_date = "2019-12-31" # Fetching a few months to test @@ -25,7 +25,7 @@ print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( data.index, - data[constants.DISCHARGE], + data[constants.DISCHARGE_DAILY_MEAN], label=gauge_id, marker="o", ) @@ -33,7 +33,7 @@ print(f"No data found for {gauge_id}") plt.xlabel(constants.TIME_INDEX) -plt.ylabel(f"{constants.DISCHARGE} (m3/s)") +plt.ylabel(f"{constants.DISCHARGE_DAILY_MEAN} (m3/s)") plt.title("Japan River Discharge - Full Time Series") plt.legend() plt.grid(True) diff --git a/examples/test_poland_fetcher.py b/examples/test_poland_fetcher.py index 782c726..45058c8 100644 --- a/examples/test_poland_fetcher.py +++ b/examples/test_poland_fetcher.py @@ -9,7 +9,7 @@ gauge_ids = [ "149180020", # CHAŁUPKI on Odra ] -variables = [constants.DISCHARGE, constants.STAGE, constants.WATER_TEMPERATURE] +variables = [constants.DISCHARGE_DAILY_MEAN, constants.STAGE_DAILY_MEAN, constants.WATER_TEMPERATURE_DAILY_MEAN] # Fetch a period from Simon's example start_date = "1999-01-01" end_date = "2001-12-31" diff --git a/examples/test_slovenia_fetcher.py b/examples/test_slovenia_fetcher.py index c3a822c..a678bd8 100644 --- a/examples/test_slovenia_fetcher.py +++ b/examples/test_slovenia_fetcher.py @@ -9,7 +9,7 @@ gauge_ids = [ "1020", # Cmurek on Mura ] -variable = constants.DISCHARGE +variable = constants.DISCHARGE_DAILY_MEAN plt.figure(figsize=(12, 6)) @@ -23,7 +23,7 @@ print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( data.index, - data[constants.DISCHARGE], + data[constants.DISCHARGE_DAILY_MEAN], label=gauge_id, marker="o", ) @@ -32,7 +32,7 @@ if "data" in locals() and not data.empty: plt.xlabel(constants.TIME_INDEX) - plt.ylabel(f"{constants.DISCHARGE} (m3/s)") + plt.ylabel(f"{constants.DISCHARGE_DAILY_MEAN} (m3/s)") plt.title(f"Slovenia River Discharge ({gauge_ids[0]} - Full Time Series)") plt.legend() plt.grid(True) diff --git a/examples/test_southafrica_fetcher.py b/examples/test_southafrica_fetcher.py index 3a98f40..0e427f5 100644 --- a/examples/test_southafrica_fetcher.py +++ b/examples/test_southafrica_fetcher.py @@ -9,21 +9,21 @@ gauge_ids = [ "X3H023", ] -variable = constants.DISCHARGE +variable = constants.DISCHARGE_DAILY_MEAN plt.figure(figsize=(12, 6)) fetcher = SouthAfricaFetcher() for gauge_id in gauge_ids: print(f"Fetching data for {gauge_id}...") - data = fetcher.get_data(gauge_id=gauge_id, variable=variable) + data = fetcher.get_data(gauge_id=gauge_id, variable=variable, start_date="2000-01-01", end_date="2005-12-31") if not data.empty: print(f"Data for {gauge_id}:") print(data.head()) print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( data.index, - data[constants.DISCHARGE], + data[constants.DISCHARGE_DAILY_MEAN], label=gauge_id, marker=".", linestyle="-", @@ -33,7 +33,7 @@ if "data" in locals() and not data.empty: plt.xlabel(constants.TIME_INDEX) - plt.ylabel(f"{constants.DISCHARGE} (m3/s)") + plt.ylabel(f"{constants.DISCHARGE_DAILY_MEAN} (m3/s)") plt.title(f"South Africa River Discharge ({gauge_ids[0]} - Full Time Series)") plt.legend() plt.grid(True) diff --git a/examples/test_uk_ea_fetcher.py b/examples/test_uk_ea_fetcher.py index e528193..3b0f92a 100644 --- a/examples/test_uk_ea_fetcher.py +++ b/examples/test_uk_ea_fetcher.py @@ -7,7 +7,7 @@ ] start_date = "2024-01-01" end_date = "2024-01-31" -variable = constants.DISCHARGE +variable = constants.DISCHARGE_DAILY_MEAN plt.figure(figsize=(12, 6)) @@ -20,14 +20,14 @@ print(data.head()) plt.plot( data.index, - data[constants.DISCHARGE], + data[constants.DISCHARGE_DAILY_MEAN], label=gauge_id.split("/")[-1], ) else: print(f"No data found for {gauge_id}") plt.xlabel(constants.TIME_INDEX) -plt.ylabel(f"{constants.DISCHARGE} (m3/s)") +plt.ylabel(f"{constants.DISCHARGE_DAILY_MEAN} (m3/s)") plt.title("UK River Discharge - Full Time Series") plt.legend() plt.grid(True) diff --git a/examples/test_uk_nrfa_fetcher.py b/examples/test_uk_nrfa_fetcher.py index 54c6894..bcf0e0b 100644 --- a/examples/test_uk_nrfa_fetcher.py +++ b/examples/test_uk_nrfa_fetcher.py @@ -9,7 +9,7 @@ gauge_ids = [ "1001", # Sample gauge from issue #34 ] -variable = constants.DISCHARGE +variable = constants.DISCHARGE_DAILY_MEAN start_date = "2022-01-01" end_date = "2022-01-31" @@ -34,7 +34,7 @@ print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( data.index, - data[constants.DISCHARGE], + data[constants.DISCHARGE_DAILY_MEAN], label=gauge_id, marker=".", linestyle="-", @@ -44,7 +44,7 @@ if "data" in locals() and not data.empty: plt.xlabel(constants.TIME_INDEX) - plt.ylabel(f"{constants.DISCHARGE} (m3/s)") + plt.ylabel(f"{constants.DISCHARGE_DAILY_MEAN} (m3/s)") plt.title(f"UK NRFA River Discharge ({gauge_ids[0]} - {start_date} to {end_date})") plt.legend() plt.grid(True) diff --git a/examples/test_usa_fetcher.py b/examples/test_usa_fetcher.py index da6ba17..82cfa1d 100644 --- a/examples/test_usa_fetcher.py +++ b/examples/test_usa_fetcher.py @@ -9,7 +9,7 @@ gauge_ids = [ "07374000", ] -variable = constants.DISCHARGE +variable = constants.DISCHARGE_DAILY_MEAN # Fetch a recent period for testing start_date = "1950-01-01" end_date = None @@ -26,7 +26,7 @@ print(f"Time series from {data.index.min()} to {data.index.max()}") plt.plot( data.index, - data[constants.DISCHARGE], + data[constants.DISCHARGE_DAILY_MEAN], label=gauge_id, marker="o", ) @@ -34,7 +34,7 @@ print(f"No data found for {gauge_id}") plt.xlabel(constants.TIME_INDEX) -plt.ylabel(f"{constants.DISCHARGE} (m3/s)") +plt.ylabel(f"{constants.DISCHARGE_DAILY_MEAN} (m3/s)") plt.title(f"USA River Discharge ({gauge_ids[0]} - 1950 to Present)") plt.legend() plt.grid(True) diff --git a/rivretrieve/australia.py b/rivretrieve/australia.py index 4a1b445..e6da17e 100644 --- a/rivretrieve/australia.py +++ b/rivretrieve/australia.py @@ -25,7 +25,7 @@ def get_gauge_ids() -> pd.DataFrame: @staticmethod def get_available_variables() -> tuple[str, ...]: - return (constants.DISCHARGE, constants.STAGE) + return (constants.DISCHARGE_DAILY_MEAN, constants.STAGE_DAILY_MEAN) def _make_bom_request(self, params: Dict[str, Any]) -> Any: """Helper function to make requests to the BoM API.""" @@ -51,10 +51,10 @@ def _make_bom_request(self, params: Dict[str, Any]) -> Any: def _get_timeseries_id(self, gauge_id: str, variable: str) -> Optional[str]: """Retrieves the timeseries ID for the given site and variable.""" - if variable == constants.STAGE: + if variable == constants.STAGE_DAILY_MEAN: bom_variable = "Water Course Level" # ts_name = "H.Merged.DailyMean" - elif variable == constants.DISCHARGE: + elif variable == constants.DISCHARGE_DAILY_MEAN: bom_variable = "Water Course Discharge" # ts_name = "Q.Merged.DailyMean" else: diff --git a/rivretrieve/canada.py b/rivretrieve/canada.py index 589c414..16599d2 100644 --- a/rivretrieve/canada.py +++ b/rivretrieve/canada.py @@ -33,7 +33,7 @@ def get_gauge_ids() -> pd.DataFrame: @staticmethod def get_available_variables() -> tuple[str, ...]: - return (constants.DISCHARGE, constants.STAGE) + return (constants.DISCHARGE_DAILY_MEAN, constants.STAGE_DAILY_MEAN) def _find_latest_hydat_link(self) -> Optional[str]: s = utils.requests_retry_session() @@ -130,8 +130,8 @@ def get_data( raise ValueError(f"Unsupported variable: {variable}") var_map = { - constants.DISCHARGE: {"table": "DLY_FLOWS", "prefix": "FLOW"}, - constants.STAGE: {"table": "DLY_LEVELS", "prefix": "LEVEL"}, + constants.DISCHARGE_DAILY_MEAN: {"table": "DLY_FLOWS", "prefix": "FLOW"}, + constants.STAGE_DAILY_MEAN: {"table": "DLY_LEVELS", "prefix": "LEVEL"}, } table = var_map[variable]["table"] diff --git a/rivretrieve/chile.py b/rivretrieve/chile.py index 0ad25de..4388fef 100644 --- a/rivretrieve/chile.py +++ b/rivretrieve/chile.py @@ -24,7 +24,7 @@ def get_gauge_ids() -> pd.DataFrame: @staticmethod def get_available_variables() -> tuple[str, ...]: - return (constants.DISCHARGE,) + return (constants.DISCHARGE_DAILY_MEAN,) def _download_data( self, @@ -33,9 +33,8 @@ def _download_data( start_date: str, end_date: str, ) -> Optional[pd.DataFrame]: - """Downloads the raw CSV data by first finding the download link.""" - if variable != constants.DISCHARGE: - logger.warning(f"ChileFetcher only supports variable='{constants.DISCHARGE}'") + if variable != constants.DISCHARGE_DAILY_MEAN: + logger.warning(f"ChileFetcher only supports variable='{constants.DISCHARGE_DAILY_MEAN}'") return None # This long URL was extracted from the R code @@ -118,8 +117,8 @@ def get_data( end_date: Optional[str] = None, ) -> pd.DataFrame: """Fetches and parses Chilean river gauge data.""" - if variable != constants.DISCHARGE: - logger.warning(f"ChileFetcher only supports variable='{constants.DISCHARGE}'") + if variable != constants.DISCHARGE_DAILY_MEAN: + logger.warning(f"ChileFetcher only supports variable='{constants.DISCHARGE_DAILY_MEAN}'") return pd.DataFrame(columns=[constants.TIME_INDEX, variable]) start_date = utils.format_start_date(start_date) diff --git a/rivretrieve/constants.py b/rivretrieve/constants.py index 04384b4..f76dc2f 100644 --- a/rivretrieve/constants.py +++ b/rivretrieve/constants.py @@ -1,16 +1,10 @@ """Module for global constants.""" -# Index +# Index. GAUGE_ID = "gauge_id" TIME_INDEX = "time" -# Data Variables -DISCHARGE = "discharge" -STAGE = "stage" -WATER_TEMPERATURE = "water_temperature" -CATCHMENT_PRECIPITATION = "catchment_precipitation" - -# Attributes +# Attributes. ALTITUDE = "altitude" AREA = "area" COUNTRY = "country" @@ -19,3 +13,43 @@ RIVER = "river" SOURCE = "source" STATION_NAME = "station_name" + +# General list of variables. +DISCHARGE = "discharge" +STAGE = "stage" +_WATER_TEMPERATURE = "water-temperature" +_CATCHMENT_PRECIPITATION = "catchment-precipitation" + +# List of temporal resolutions. +DAILY = "daily" +_HOURLY = "60min" +_15MIN = "15min" +INSTANTANEOUS = "instantaneous" + +# List of temporal aggregrations. +_MEAN = "mean" +_MIN = "min" +_MAX = "max" +_SUM = "sum" + +# ------------------------------ Supported set of variables ---------------------------------------- + +# Discharge. +DISCHARGE_DAILY_MEAN = f"{DISCHARGE}_{DAILY}_{_MEAN}" +DISCHARGE_DAILY_MAX = f"{DISCHARGE}_{DAILY}_{_MAX}" +DISCHARGE_DAILY_MIN = f"{DISCHARGE}_{DAILY}_{_MIN}" +DISCHARGE_15MIN = f"{DISCHARGE}_{_15MIN}" +DISCHARGE_INSTANT = f"{DISCHARGE}_{INSTANTANEOUS}" + +# Stage. +STAGE_DAILY_MEAN = f"{STAGE}_{DAILY}_{_MEAN}" +STAGE_DAILY_MAX = f"{STAGE}_{DAILY}_{_MAX}" +STAGE_DAILY_MIN = f"{STAGE}_{DAILY}_{_MIN}" +STAGE_15MIN = f"{STAGE}_{_15MIN}" +STAGE_INSTANT = f"{STAGE}_{INSTANTANEOUS}" + +# Water temperature. +WATER_TEMPERATURE_DAILY_MEAN = f"{_WATER_TEMPERATURE}_{DAILY}_{_MEAN}" + +# Precipitation +CATCHMENT_PRECIPITATION_DAILY_SUM = f"{_CATCHMENT_PRECIPITATION}_{DAILY}_{_SUM}" diff --git a/rivretrieve/france.py b/rivretrieve/france.py index 64791fa..dc0d47f 100644 --- a/rivretrieve/france.py +++ b/rivretrieve/france.py @@ -23,7 +23,21 @@ def get_gauge_ids() -> pd.DataFrame: @staticmethod def get_available_variables() -> tuple[str, ...]: - return (constants.DISCHARGE, constants.STAGE) + return (constants.DISCHARGE_DAILY_MEAN, constants.STAGE_DAILY_MAX) + + def _get_variable_code(self, variable: str) -> str: + if variable == constants.DISCHARGE_DAILY_MEAN: + return "QmnJ" + elif variable == constants.STAGE_DAILY_MAX: + return "HIXnJ" + else: + logger.warning(f"Unsupported variable: {variable}") + + def _conversion_factor(self, variable: str) -> float: + if variable.startswith(constants.DISCHARGE): + return 1000 # l/s for flow rates (divide by 1000 to convert to m3/s). + elif variable.startswith(constants.STAGE): + return 1000 # mm for water heights (divide by 1000 to convert to meters); def _download_data( self, @@ -33,14 +47,8 @@ def _download_data( end_date: str, ) -> List[Dict[str, Any]]: """Downloads raw data from the Hubeau API.""" - if variable == constants.DISCHARGE: - grandeur = "QmnJ" - elif variable == constants.STAGE: - grandeur = "HnJ" # Assuming daily mean stage, though doc mentions HIXnJ - logger.warning("Using grandeur_hydro='HnJ' for stage, this might not be daily mean.") - else: - logger.warning(f"Unsupported variable: {variable}") - return [] + grandeur = self._get_variable_code(variable) + params = { "code_entite": gauge_id, "date_debut_obs": start_date, @@ -92,7 +100,7 @@ def _parse_data( if df.empty: return pd.DataFrame(columns=[constants.TIME_INDEX, variable]) - grandeur_code = "QmnJ" if variable == constants.DISCHARGE else "HnJ" + grandeur_code = self._get_variable_code(variable) df = df[df["grandeur_hydro_elab"] == grandeur_code] if df.empty or "date_obs_elab" not in df.columns or "resultat_obs_elab" not in df.columns: @@ -100,8 +108,7 @@ def _parse_data( return pd.DataFrame(columns=[constants.TIME_INDEX, variable]) df[constants.TIME_INDEX] = pd.to_datetime(df["date_obs_elab"]).dt.date - # Convert L/s to m3/s - df[variable] = pd.to_numeric(df["resultat_obs_elab"], errors="coerce") / 1000.0 + df[variable] = pd.to_numeric(df["resultat_obs_elab"], errors="coerce") / self._conversion_factor(variable) df[constants.TIME_INDEX] = pd.to_datetime(df[constants.TIME_INDEX]) return ( df[[constants.TIME_INDEX, variable]] diff --git a/rivretrieve/japan.py b/rivretrieve/japan.py index eb818c2..13fc1f9 100644 --- a/rivretrieve/japan.py +++ b/rivretrieve/japan.py @@ -27,12 +27,12 @@ def get_gauge_ids() -> pd.DataFrame: @staticmethod def get_available_variables() -> tuple[str, ...]: - return (constants.DISCHARGE, constants.STAGE) + return (constants.DISCHARGE_DAILY_MEAN, constants.STAGE_DAILY_MEAN) def _get_kind(self, variable: str) -> int: - if variable == constants.STAGE: + if variable == constants.STAGE_DAILY_MEAN: return 2 - elif variable == constants.DISCHARGE: + elif variable == constants.DISCHARGE_DAILY_MEAN: return 6 else: raise ValueError(f"Unsupported variable: {variable}") diff --git a/rivretrieve/poland.py b/rivretrieve/poland.py index f8619e8..7f08532 100644 --- a/rivretrieve/poland.py +++ b/rivretrieve/poland.py @@ -31,7 +31,7 @@ def get_gauge_ids() -> pd.DataFrame: @staticmethod def get_available_variables() -> tuple[str, ...]: - return (constants.DISCHARGE, constants.STAGE, constants.WATER_TEMPERATURE) + return (constants.DISCHARGE_DAILY_MEAN, constants.STAGE_DAILY_MEAN, constants.WATER_TEMPERATURE_DAILY_MEAN) def _get_metadata_headers(self): """Fetches and cleans metadata headers.""" @@ -109,9 +109,9 @@ def _parse_all_data(self, raw_data_list: List[pd.DataFrame]) -> pd.DataFrame: full_df = full_df.rename( columns={ "Kod stacji": constants.GAUGE_ID, - "Przepływ [m3/s]": constants.DISCHARGE, - "Stan wody [cm]": constants.STAGE, - "Temperatura wody [st. C]": constants.WATER_TEMPERATURE, + "Przepływ [m3/s]": constants.DISCHARGE_DAILY_MEAN, + "Stan wody [cm]": constants.STAGE_DAILY_MEAN, + "Temperatura wody [st. C]": constants.WATER_TEMPERATURE_DAILY_MEAN, } ) @@ -130,16 +130,16 @@ def _parse_all_data(self, raw_data_list: List[pd.DataFrame]) -> pd.DataFrame: # Select and convert variables var_cols = [ - constants.DISCHARGE, - constants.STAGE, - constants.WATER_TEMPERATURE, + constants.DISCHARGE_DAILY_MEAN, + constants.STAGE_DAILY_MEAN, + constants.WATER_TEMPERATURE_DAILY_MEAN, ] for var in var_cols: if var in full_df.columns: full_df[var] = pd.to_numeric(full_df[var], errors="coerce") - if constants.STAGE in full_df.columns: - full_df[constants.STAGE] = full_df[constants.STAGE] / 100.0 # cm to m + if constants.STAGE_DAILY_MEAN in full_df.columns: + full_df[constants.STAGE_DAILY_MEAN] = full_df[constants.STAGE_DAILY_MEAN] / 100.0 # cm to m # Clean placeholder values full_df.replace({9999: None, 99999.999: None, 99.9: None, 999: None}, inplace=True) diff --git a/rivretrieve/slovenia.py b/rivretrieve/slovenia.py index 4fb0f44..f99dd81 100644 --- a/rivretrieve/slovenia.py +++ b/rivretrieve/slovenia.py @@ -25,7 +25,7 @@ def get_gauge_ids() -> pd.DataFrame: @staticmethod def get_available_variables() -> tuple[str, ...]: - return (constants.DISCHARGE, constants.STAGE) + return (constants.DISCHARGE_DAILY_MEAN, constants.STAGE_DAILY_MEAN) def _download_data(self, gauge_id: str, variable: str, start_date: str, end_date: str) -> Optional[str]: """Downloads the raw CSV data from the ARSO API.""" @@ -63,14 +63,14 @@ def _parse_data(self, gauge_id: str, raw_data: Optional[str], variable: str) -> df[constants.TIME_INDEX] = pd.to_datetime(df[constants.TIME_INDEX], format="%d.%m.%Y", errors="coerce") df = df.dropna(subset=[constants.TIME_INDEX]) - if variable == constants.STAGE: + if variable == constants.STAGE_DAILY_MEAN: raw_col = "vodostaj (cm)" if raw_col in df.columns: df[variable] = pd.to_numeric(df[raw_col], errors="coerce") / 100.0 # cm to m else: logger.warning(f"Column {raw_col} not found for site {gauge_id}") return pd.DataFrame(columns=[constants.TIME_INDEX, variable]) - elif variable == constants.DISCHARGE: + elif variable == constants.DISCHARGE_DAILY_MEAN: raw_col = "pretok (m3/s)" if raw_col in df.columns: df[variable] = pd.to_numeric(df[raw_col], errors="coerce") diff --git a/rivretrieve/southafrica.py b/rivretrieve/southafrica.py index 450e8ac..3973618 100644 --- a/rivretrieve/southafrica.py +++ b/rivretrieve/southafrica.py @@ -27,7 +27,7 @@ def get_gauge_ids() -> pd.DataFrame: @staticmethod def get_available_variables() -> tuple[str, ...]: - return (constants.DISCHARGE, constants.STAGE) + return (constants.DISCHARGE_DAILY_MEAN, constants.DISCHARGE_INSTANT, constants.STAGE_INSTANT) def _construct_endpoint( self, @@ -44,6 +44,16 @@ def _construct_endpoint( ) return endpoint + def _get_variable_name(self, variable: str) -> str: + if variable == constants.DISCHARGE_DAILY_MEAN: + return "D_AVG_FR" + elif variable == constants.DISCHARGE_INSTANT: + return "COR_FLOW" + elif variable == constants.STAGE_INSTANT: + return "COR_LEVEL" + else: + raise ValueError(f"Unsupported variable: {variable}") + def _download_data( self, gauge_id: str, @@ -58,18 +68,18 @@ def _download_data( headers = {"User-Agent": "Mozilla/5.0"} data_list = [] - if variable == constants.STAGE: + if variable in (constants.STAGE_INSTANT, constants.DISCHARGE_INSTANT): data_type = "Point" chunk_years = 1 header = [ "DATE", "TIME", - "COR_LEVEL", + "COR_LEVEL", # Stage instant. "COR_LEVEL_QUAL", - "COR_FLOW", + "COR_FLOW", # Flow instant. "COR_FLOW_QUAL", ] - elif variable == constants.DISCHARGE: # discharge + elif variable == constants.DISCHARGE_DAILY_MEAN: # discharge data_type = "Daily" chunk_years = 20 header = ["DATE", "D_AVG_FR", "QUAL"] @@ -139,23 +149,21 @@ def _parse_data( return pd.DataFrame(columns=[constants.TIME_INDEX, variable]) try: - full_df = pd.concat(raw_data_list, ignore_index=True) - if full_df.empty: + df = pd.concat(raw_data_list, ignore_index=True) + if df.empty: return pd.DataFrame(columns=[constants.TIME_INDEX, variable]) - full_df[constants.TIME_INDEX] = pd.to_datetime(full_df["DATE"], format="%Y%m%d", errors="coerce") - full_df = full_df.dropna(subset=[constants.TIME_INDEX]) + df[constants.TIME_INDEX] = pd.to_datetime(df["DATE"], format="%Y%m%d", errors="coerce") + df = ( + df.dropna(subset=[constants.TIME_INDEX]) + .sort_values(by=constants.TIME_INDEX) + .set_index(constants.TIME_INDEX) + ) - if variable == constants.STAGE: - full_df["COR_LEVEL"] = pd.to_numeric(full_df["COR_LEVEL"], errors="coerce") - # Average stage if multiple readings per day - daily_df = full_df.groupby(constants.TIME_INDEX).agg(Value=("COR_LEVEL", "mean")).reset_index() - else: # discharge - full_df["D_AVG_FR"] = pd.to_numeric(full_df["D_AVG_FR"], errors="coerce") - daily_df = full_df[[constants.TIME_INDEX, "D_AVG_FR"]].rename(columns={"D_AVG_FR": "Value"}) + column = self._get_variable_name(variable) + df[column] = pd.to_numeric(df[column], errors="coerce") - daily_df = daily_df.rename(columns={"Value": variable}) - return daily_df.dropna().sort_values(by=constants.TIME_INDEX).set_index(constants.TIME_INDEX) + return df.rename(columns={column: variable}) except Exception as e: logger.error(f"Error parsing data for site {gauge_id}: {e}") diff --git a/rivretrieve/uk_ea.py b/rivretrieve/uk_ea.py index 808013e..2e30f5a 100644 --- a/rivretrieve/uk_ea.py +++ b/rivretrieve/uk_ea.py @@ -34,7 +34,7 @@ def get_gauge_ids() -> pd.DataFrame: @staticmethod def get_available_variables() -> tuple[str, ...]: - return (constants.DISCHARGE, constants.STAGE) + return (constants.DISCHARGE_DAILY_MEAN, constants.STAGE_15MIN) def get_metadata(self) -> pd.DataFrame: """Fetches site metadata for all stations from the EA API. @@ -66,9 +66,9 @@ def get_metadata(self) -> pd.DataFrame: def _get_measure_notation(self, variable: str) -> str: """Gets the notation for the given variable.""" - if variable == constants.STAGE: + if variable == constants.STAGE_15MIN: return "level-i-900-m-qualified" - elif variable == constants.DISCHARGE: + elif variable == constants.DISCHARGE_DAILY_MEAN: return "flow-m-86400-m3s-qualified" else: raise ValueError(f"Unsupported variable: {variable}") @@ -129,7 +129,7 @@ def _download_data(self, gauge_id: str, variable: str, start_date: str, end_date return all_items - def _parse_data(self, gauge_id: str, raw_data: List[Dict[str, Any]], variable: str) -> pd.DataFrame: + def _parse_data(self, raw_data: List[Dict[str, Any]], variable: str) -> pd.DataFrame: """Parses the raw JSON data into a pandas DataFrame.""" if not raw_data: return pd.DataFrame(columns=[constants.TIME_INDEX, variable]) @@ -138,32 +138,22 @@ def _parse_data(self, gauge_id: str, raw_data: List[Dict[str, Any]], variable: s df[constants.TIME_INDEX] = pd.to_datetime(df["dateTime"]).dt.date df["Value"] = pd.to_numeric(df["value"], errors="coerce") - if variable == constants.STAGE: - # UK stage data is 15-min, average to daily - # A full day has 24 * 4 = 96 readings. We accept days with at least 90 readings. - min_readings = 90 - df_daily = ( - df.groupby(constants.TIME_INDEX).agg(Value=("Value", "mean"), Count=("Value", "size")).reset_index() - ) - df_daily = df_daily[df_daily["Count"] >= min_readings] - df_daily = df_daily[[constants.TIME_INDEX, "Value"]] - else: # discharge is already daily - df_daily = df[[constants.TIME_INDEX, "Value"]] + df = df[[constants.TIME_INDEX, "Value"]] - df_daily = df_daily.rename(columns={"Value": variable}) - df_daily[constants.TIME_INDEX] = pd.to_datetime(df_daily[constants.TIME_INDEX]) + df = df.rename(columns={"Value": variable}) + df[constants.TIME_INDEX] = pd.to_datetime(df[constants.TIME_INDEX]) # Ensure complete time series within the data range - if not df_daily.empty: + if not df.empty: date_range = pd.date_range( - start=df_daily[constants.TIME_INDEX].min(), - end=df_daily[constants.TIME_INDEX].max(), + start=df[constants.TIME_INDEX].min(), + end=df[constants.TIME_INDEX].max(), freq="D", ) complete_ts = pd.DataFrame(date_range, columns=[constants.TIME_INDEX]) - df_daily = pd.merge(complete_ts, df_daily, on=constants.TIME_INDEX, how="left") + df = pd.merge(complete_ts, df, on=constants.TIME_INDEX, how="left") - return df_daily.set_index(constants.TIME_INDEX) + return df.set_index(constants.TIME_INDEX) def get_data( self, @@ -180,7 +170,7 @@ def get_data( try: raw_data = self._download_data(gauge_id, variable, start_date, end_date) - df = self._parse_data(gauge_id, raw_data, variable) + df = self._parse_data(raw_data, variable) # Filter by exact start and end date after processing start_date_dt = pd.to_datetime(start_date) diff --git a/rivretrieve/uk_nrfa.py b/rivretrieve/uk_nrfa.py index 18747a4..9691a98 100644 --- a/rivretrieve/uk_nrfa.py +++ b/rivretrieve/uk_nrfa.py @@ -60,12 +60,12 @@ def get_metadata(self) -> pd.DataFrame: @staticmethod def get_available_variables() -> tuple[str, ...]: # Based on common NRFA data types, can be expanded - return (constants.DISCHARGE, constants.CATCHMENT_PRECIPITATION) + return (constants.DISCHARGE_DAILY_MEAN, constants.CATCHMENT_PRECIPITATION_DAILY_SUM) def _get_nrfa_data_type(self, variable: str) -> str: - if variable == constants.DISCHARGE: + if variable == constants.DISCHARGE_DAILY_MEAN: return "gdf" # Mean daily flow - elif variable == constants.CATCHMENT_PRECIPITATION: + elif variable == constants.CATCHMENT_PRECIPITATION_DAILY_SUM: return "cdr" # Catchment daily precipitation. else: raise ValueError(f"Unsupported variable: {variable} for NRFA") diff --git a/rivretrieve/usa.py b/rivretrieve/usa.py index 0ded443..733594a 100644 --- a/rivretrieve/usa.py +++ b/rivretrieve/usa.py @@ -21,26 +21,56 @@ def get_gauge_ids() -> pd.DataFrame: @staticmethod def get_available_variables() -> tuple[str, ...]: - return (constants.DISCHARGE, constants.STAGE) + return ( + constants.DISCHARGE_DAILY_MEAN, + constants.DISCHARGE_INSTANT, + constants.STAGE_DAILY_MEAN, + constants.STAGE_DAILY_MAX, + constants.STAGE_DAILY_MIN, + constants.STAGE_INSTANT, + ) def _get_param_code(self, variable: str) -> str: - if variable == constants.STAGE: + if constants.STAGE in variable: return "00065" - elif variable == constants.DISCHARGE: + elif constants.DISCHARGE in variable: return "00060" else: raise ValueError(f"Unsupported variable: {variable}") + def _get_column_name(self, variable: str) -> str: + param_code = self._get_param_code(variable) + if variable == constants.STAGE_DAILY_MAX: + return f"{param_code}_Maximum" + elif variable == constants.STAGE_DAILY_MIN: + return f"{param_code}_Minimum" + elif variable == constants.STAGE_DAILY_MEAN: + return f"{param_code}_Mean" + elif variable == constants.DISCHARGE_DAILY_MEAN: + return f"{param_code}_Mean" + elif variable == constants.DISCHARGE_INSTANT: + return param_code + elif variable == constants.STAGE_INSTANT: + return param_code + def _download_data(self, gauge_id: str, variable: str, start_date: str, end_date: str) -> pd.DataFrame: """Downloads data using the dataretrieval package.""" param_code = self._get_param_code(variable) try: - df, meta = nwis.get_dv( - sites=gauge_id, - startDT=start_date, - endDT=end_date, - parameterCd=[param_code], - ) + if constants.DAILY in variable: + df, meta = nwis.get_dv( + sites=gauge_id, + startDT=start_date, + endDT=end_date, + parameterCd=[param_code], + ) + elif constants.INSTANTANEOUS in variable: + df, meta = nwis.get_iv( + sites=gauge_id, + startDT=start_date, + endDT=end_date, + parameterCd=[param_code], + ) return df except Exception as e: logger.error(f"Error fetching NWIS data for site {gauge_id}, param {param_code}: {e}") @@ -53,15 +83,11 @@ def _parse_data(self, gauge_id: str, raw_data: pd.DataFrame, variable: str) -> p return pd.DataFrame(columns=[constants.TIME_INDEX, variable]) param_code = self._get_param_code(variable) - - value_col = None - for col in raw_data.columns: - if col.startswith(param_code) and ("_Mean" in col or "_00003" in col): - value_col = col - break - - if value_col is None: - logger.warning(f"Could not find value column for param {param_code} in data for site {gauge_id}") + value_col = self._get_column_name(variable) + if value_col not in raw_data.columns: + logger.warning( + f"Could not find value column {value_col} for param {param_code} in data for site {gauge_id}" + ) return pd.DataFrame(columns=[constants.TIME_INDEX, variable]) df = raw_data[[value_col]].copy() @@ -70,9 +96,9 @@ def _parse_data(self, gauge_id: str, raw_data: pd.DataFrame, variable: str) -> p df[constants.TIME_INDEX] = pd.to_datetime(df[constants.TIME_INDEX].dt.date) # Unit conversion - if variable == constants.STAGE: # Feet to meters + if variable.startswith(constants.STAGE): # Feet to meters mult = 0.3048 - elif variable == constants.DISCHARGE: # cfs to m3/s + elif variable.startswith(constants.DISCHARGE): # cfs to m3/s mult = 0.0283168466 df[variable] = pd.to_numeric(df[value_col], errors="coerce") * mult diff --git a/tests/test_australia.py b/tests/test_australia.py index 9f63588..d12254a 100644 --- a/tests/test_australia.py +++ b/tests/test_australia.py @@ -37,7 +37,7 @@ def bom_request_side_effect(params): mock_make_bom_request.side_effect = bom_request_side_effect gauge_id = "405212" - variable = constants.DISCHARGE + variable = constants.DISCHARGE_DAILY_MEAN start_date = "2010-01-01" end_date = "2010-01-03" @@ -45,7 +45,7 @@ def bom_request_side_effect(params): expected_data = { constants.TIME_INDEX: pd.to_datetime(["2010-01-01", "2010-01-02", "2010-01-03"]), - constants.DISCHARGE: [0.000, 3.710, 3.211], + constants.DISCHARGE_DAILY_MEAN: [0.000, 3.710, 3.211], } expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) diff --git a/tests/test_canada.py b/tests/test_canada.py index 85c2079..3b879fb 100644 --- a/tests/test_canada.py +++ b/tests/test_canada.py @@ -24,7 +24,7 @@ def test_get_data_discharge(self, mock_hydat_path, mock_download, mock_requests) mock_download.return_value = True # Prevent download attempt gauge_id = "08GA031" - variable = constants.DISCHARGE + variable = constants.DISCHARGE_DAILY_MEAN start_date = "2010-01-01" end_date = "2010-01-05" @@ -34,7 +34,7 @@ def test_get_data_discharge(self, mock_hydat_path, mock_download, mock_requests) constants.TIME_INDEX: pd.to_datetime( ["2010-01-01", "2010-01-02", "2010-01-03", "2010-01-04", "2010-01-05"] ), - constants.DISCHARGE: [1.1, 1.2, 1.3, 1.4, 1.5], + constants.DISCHARGE_DAILY_MEAN: [1.1, 1.2, 1.3, 1.4, 1.5], } expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) @@ -50,7 +50,7 @@ def test_get_data_stage(self, mock_hydat_path, mock_download, mock_requests): mock_download.return_value = True # Prevent download attempt gauge_id = "08GA031" - variable = constants.STAGE + variable = constants.STAGE_DAILY_MEAN start_date = "2010-01-01" end_date = "2010-01-05" @@ -60,7 +60,7 @@ def test_get_data_stage(self, mock_hydat_path, mock_download, mock_requests): constants.TIME_INDEX: pd.to_datetime( ["2010-01-01", "2010-01-02", "2010-01-03", "2010-01-04", "2010-01-05"] ), - constants.STAGE: [10.1, 10.2, 10.3, 10.4, 10.5], + constants.STAGE_DAILY_MEAN: [10.1, 10.2, 10.3, 10.4, 10.5], } expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) diff --git a/tests/test_chile.py b/tests/test_chile.py index cd86075..09dd473 100644 --- a/tests/test_chile.py +++ b/tests/test_chile.py @@ -40,7 +40,7 @@ def get_side_effect(*args, **kwargs): mock_get.side_effect = get_side_effect gauge_id = "test_gauge" - variable = constants.DISCHARGE + variable = constants.DISCHARGE_DAILY_MEAN start_date = "2022-01-01" end_date = "2022-01-03" @@ -48,7 +48,7 @@ def get_side_effect(*args, **kwargs): expected_data = { constants.TIME_INDEX: pd.to_datetime(["2022-01-01", "2022-01-02", "2022-01-03"]), - constants.DISCHARGE: [15.5, 16.0, 15.8], + constants.DISCHARGE_DAILY_MEAN: [15.5, 16.0, 15.8], } expected_df = pd.DataFrame(expected_data).set_index(constants.TIME_INDEX) diff --git a/tests/test_data/poland_test.zarr.zip b/tests/test_data/poland_test.zarr.zip index 11b7a1e30eaf3549a06bb99cf2adef615cd7c417..29f63ebf84b48f4d157df683a191a4a931d9b6ab 100644 GIT binary patch literal 6955 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 K;h!xy^X+dmcY( Date: Fri, 17 Oct 2025 10:44:52 +0000 Subject: [PATCH 3/3] Change 15min name to instantaneous --- rivretrieve/constants.py | 4 ---- rivretrieve/uk_ea.py | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/rivretrieve/constants.py b/rivretrieve/constants.py index f76dc2f..ad16ac0 100644 --- a/rivretrieve/constants.py +++ b/rivretrieve/constants.py @@ -22,8 +22,6 @@ # List of temporal resolutions. DAILY = "daily" -_HOURLY = "60min" -_15MIN = "15min" INSTANTANEOUS = "instantaneous" # List of temporal aggregrations. @@ -38,14 +36,12 @@ DISCHARGE_DAILY_MEAN = f"{DISCHARGE}_{DAILY}_{_MEAN}" DISCHARGE_DAILY_MAX = f"{DISCHARGE}_{DAILY}_{_MAX}" DISCHARGE_DAILY_MIN = f"{DISCHARGE}_{DAILY}_{_MIN}" -DISCHARGE_15MIN = f"{DISCHARGE}_{_15MIN}" DISCHARGE_INSTANT = f"{DISCHARGE}_{INSTANTANEOUS}" # Stage. STAGE_DAILY_MEAN = f"{STAGE}_{DAILY}_{_MEAN}" STAGE_DAILY_MAX = f"{STAGE}_{DAILY}_{_MAX}" STAGE_DAILY_MIN = f"{STAGE}_{DAILY}_{_MIN}" -STAGE_15MIN = f"{STAGE}_{_15MIN}" STAGE_INSTANT = f"{STAGE}_{INSTANTANEOUS}" # Water temperature. diff --git a/rivretrieve/uk_ea.py b/rivretrieve/uk_ea.py index 2e30f5a..986954f 100644 --- a/rivretrieve/uk_ea.py +++ b/rivretrieve/uk_ea.py @@ -34,7 +34,7 @@ def get_gauge_ids() -> pd.DataFrame: @staticmethod def get_available_variables() -> tuple[str, ...]: - return (constants.DISCHARGE_DAILY_MEAN, constants.STAGE_15MIN) + return (constants.DISCHARGE_DAILY_MEAN, constants.STAGE_INSTANT) def get_metadata(self) -> pd.DataFrame: """Fetches site metadata for all stations from the EA API. @@ -66,7 +66,7 @@ def get_metadata(self) -> pd.DataFrame: def _get_measure_notation(self, variable: str) -> str: """Gets the notation for the given variable.""" - if variable == constants.STAGE_15MIN: + if variable == constants.STAGE_INSTANT: return "level-i-900-m-qualified" elif variable == constants.DISCHARGE_DAILY_MEAN: return "flow-m-86400-m3s-qualified"