loone-data-prep 1.3.0__py3-none-any.whl → 1.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,195 @@
1
+ """
2
+ Utilities for interacting with the DBHYDRO Insights database services.
3
+
4
+ This module provides functions for fetching data from endpoints used
5
+ by the South Florida Water Management District's DBHYDRO Insights app.
6
+ """
7
+
8
+ from datetime import datetime
9
+ import requests
10
+ from typing import Literal, Tuple
11
+
12
+
13
+ def get_dbhydro_station_metadata(station_id: str) -> dict | None:
14
+ """
15
+ Fetches metadata for a specific station from the DBHYDRO_SiteStation service.
16
+
17
+ Args:
18
+ station_id (str): The ID of the station for which to fetch metadata. Examples: 'FISHP', 'L OKEE', etc.
19
+
20
+ Returns:
21
+ dict: A dictionary containing the metadata of the station, or None if the request fails.
22
+ """
23
+ # Build the request URL with the provided station ID
24
+ request_url = 'https://geoweb.sfwmd.gov/agsext2/rest/services/MonitoringLocations/DBHYDRO_SiteStation/MapServer/4/query'
25
+
26
+ params = {
27
+ 'f': 'json',
28
+ 'outFields': '*',
29
+ 'spatialRel': 'esriSpatialRelIntersects',
30
+ 'where': f"(STATION = '{station_id}')"
31
+ }
32
+
33
+ # Send the GET request to the specified URL with the parameters
34
+ try:
35
+ response = requests.get(request_url, params=params)
36
+ except requests.exceptions.RequestException:
37
+ return None
38
+
39
+ # Successful Request
40
+ if response.status_code == 200:
41
+ # Parse the JSON response
42
+ json = response.json()
43
+
44
+ # No data given back for given station ID
45
+ if not json['features']:
46
+ return None
47
+
48
+ # Data given back, return the JSON response
49
+ return json
50
+
51
+ # Failure
52
+ return None
53
+
54
+
55
+ def get_dbhydro_continuous_timeseries_metadata(
56
+ station_ids: list[str],
57
+ categories: list[str] | None = ['ALL'],
58
+ parameters: list[str] | None = ['ALL'],
59
+ statistics: list[str] | None = ['ALL'],
60
+ recorders: list[str] | None = ['ALL'],
61
+ frequencies: list[str] | None = ['ALL']
62
+ ) -> dict | None:
63
+ """Fetches metadata for continuous time series data from the DBHYDRO Insights service.
64
+
65
+ Args:
66
+ station_ids (list[str]): List of station IDs to query.
67
+ categories (list[str] | None): List of categories to filter by. Defaults to ['ALL'].
68
+ parameters (list[str] | None): List of parameters to filter by. Defaults to ['ALL'].
69
+ statistics (list[str] | None): List of statistics to filter by. Defaults to ['ALL'].
70
+ recorders (list[str] | None): List of recorders to filter by. Defaults to ['ALL'].
71
+ frequencies (list[str] | None): List of frequencies to filter by. Defaults to ['ALL'].
72
+
73
+ Returns:
74
+ dict | None: The JSON response from the API if successful, otherwise None.
75
+
76
+ Raises:
77
+ Exception: If the request fails.
78
+ """
79
+ # Build the request URL
80
+ request_url = 'https://insightsdata.api.sfwmd.gov/v1/insights-data/cont/ts'
81
+
82
+ # Build the locations list
83
+ locations = []
84
+
85
+ for station_id in station_ids:
86
+ # Build the location dictionary for this station_id
87
+ location = {
88
+ 'name': station_id,
89
+ 'type': 'STATION',
90
+ }
91
+
92
+ # Add location to the locations list
93
+ locations.append(location)
94
+
95
+ # Build the data payload
96
+ data = {
97
+ 'query': {
98
+ 'locations': locations,
99
+ 'parameters': parameters,
100
+ 'category': categories,
101
+ 'statistic': statistics,
102
+ 'recorder': recorders,
103
+ 'frequency': frequencies,
104
+ 'dbkeys': ['ALL'],
105
+ }
106
+ }
107
+
108
+ # Send the POST request to the specified URL with the parameters
109
+ response = requests.post(request_url, json=data)
110
+
111
+ # Successful Request
112
+ if response.status_code == 200:
113
+ # Parse the JSON response
114
+ json = response.json()
115
+
116
+ # No data given back for given station ID
117
+ if not json['results']:
118
+ return None
119
+
120
+ # Data given back, return the JSON response
121
+ return json
122
+
123
+ # Failure
124
+ raise Exception(f"Request failed with status code {response.status_code}: {response.text}")
125
+
126
+
127
+ def get_dbhydro_water_quality_metadata(stations: list[Tuple[str,Literal['SITE', 'STATION']]], test_numbers: list[int]) -> dict | None:
128
+ """Fetches metadata for water quality data from the DBHYDRO Insights service.
129
+
130
+ Args:
131
+ stations (list[Tuple[str, Literal['SITE', 'STATION']]]): List of tuples containing station names and station types ('SITE' or 'STATION') to get water quality metadata for.
132
+ test_numbers (list[int]): List of test numbers to get data for. Test numbers map to parameters. Example: 25 maps to 'PHOSPHATE, TOTAL AS P'.
133
+
134
+ Returns:
135
+ dict | None: The JSON response from the API if successful, otherwise None.
136
+
137
+ Raises:
138
+ Exception: If the request fails.
139
+ """
140
+ # Build the request URL
141
+ request_url = 'https://insightsdata.api.sfwmd.gov/v1/insights-data/chem/ts'
142
+
143
+ # Build the locations list
144
+ locations = []
145
+
146
+ for station in stations:
147
+ # Build the location dictionary for this station/site
148
+ location = {
149
+ 'name': station[0],
150
+ 'type': station[1],
151
+ }
152
+
153
+ # Add location to the locations list
154
+ locations.append(location)
155
+
156
+ # Build the query parameters
157
+ query_parameters = {
158
+ 'offset': 0,
159
+ 'limit': 1000,
160
+ 'sort': 'project,location,parameterDesc,matrix,method',
161
+ 'startDate': '19000101',
162
+ 'endDate': datetime.now().strftime("%Y%m%d"),
163
+ 'period': '',
164
+ }
165
+
166
+ # Build the data payload
167
+ payload = {
168
+ 'query': {
169
+ 'locations': locations,
170
+ 'matrices': ['ALL'],
171
+ 'methods': ['ALL'],
172
+ 'paramGroups': ['ALL'],
173
+ 'parameters': [str(num) for num in test_numbers],
174
+ 'projects': ['ALL'],
175
+ 'sampleTypes': ['ALL'],
176
+ }
177
+ }
178
+
179
+ # Send the POST request to the specified URL with the parameters
180
+ response = requests.post(request_url, params=query_parameters, json=payload)
181
+
182
+ # Successful Request
183
+ if response.status_code == 200:
184
+ # Parse the JSON response
185
+ json = response.json()
186
+
187
+ # No data given back for given station ID
188
+ if not json['results']:
189
+ return None
190
+
191
+ # Data given back, return the JSON response
192
+ return json
193
+
194
+ # Failure
195
+ raise Exception(f"Request failed with status code {response.status_code}: {response.text}")
@@ -1,74 +1,73 @@
1
1
  import sys
2
2
  from retry import retry
3
- from rpy2.robjects import r
4
- from rpy2.rinterface_lib.embedded import RRuntimeError
5
3
  import pandas as pd
4
+ from loone_data_prep.utils import df_replace_missing_with_nan, get_dbhydro_api
6
5
 
7
6
 
8
- @retry(RRuntimeError, tries=5, delay=15, max_delay=60, backoff=2)
7
+ @retry(Exception, tries=5, delay=15, max_delay=60, backoff=2)
9
8
  def get(
10
9
  workspace,
11
10
  date_min: str = "1972-01-01",
12
11
  date_max: str = "2023-06-30"
13
12
  ) -> None:
14
- r(
15
- f"""
16
- # Load the required libraries
17
- library(dbhydroR)
18
- library(dplyr)
19
-
20
- # Helper Functions
21
- retrieve_data <- function(dbkey, date_min, date_max)
22
- {{
23
- # Get the data from dbhydro
24
- df = get_hydro(dbkey = dbkey, date_min = date_min, date_max = date_max, raw = TRUE)
25
-
26
- # Give data.frame correct column names so it can be cleaned using the clean_hydro function
27
- colnames(df) <- c("station", "dbkey", "date", "data.value", "qualifer", "revision.date")
28
-
29
- # Add a type and units column to data so it can be cleaned using the clean_hydro function
30
- df$type <- "FLOW"
31
- df$units <- "cfs"
32
-
33
- # Clean the data.frame
34
- df <- clean_hydro(df)
35
-
36
- # Drop the " _FLOW_cfs" column
37
- df <- df %>% select(-` _FLOW_cfs`)
38
-
39
- # Convert Flow rate from cfs to m³/day
40
- df[, -1] <- df[, -1] * (0.0283168466 * 86400)
41
-
42
- # Return resulting data.frame
43
- return(df)
44
- }}
45
-
46
- # S65E_S
47
- S65E_S <- retrieve_data(dbkey = "91656", date_min = "{date_min}", date_max = "{date_max}")
48
-
49
- # Wait five seconds before next request to avoid "too many requests" error
50
- Sys.sleep(5)
51
-
52
- # S65EX1_S
53
- S65EX1_S <- retrieve_data(dbkey = "AL760", date_min = "{date_min}", date_max = "{date_max}")
54
-
55
- # Merge the data from each dbkey
56
- result <- merge(S65E_S, S65EX1_S, by = "date", all = TRUE)
57
-
58
- # Write the data to a file
59
- write.csv(result, file = '{workspace}/S65E_total.csv')
60
- """
61
- )
62
-
63
- _reformat_s65e_total_file(workspace)
13
+ """Retrieve total flow data for S65E structure (S65E_S + S65EX1_S) and save to CSV.
14
+
15
+ Args:
16
+ workspace (str): Path to workspace where data will be downloaded.
17
+ date_min (str): Minimum date for data retrieval in 'YYYY-MM-DD' format.
18
+ date_max (str): Maximum date for data retrieval in 'YYYY-MM-DD' format.
19
+ """
20
+ # Get a DbHydroApi instance
21
+ api = get_dbhydro_api()
22
+
23
+ # S65E_S
24
+ s65e_s = api.get_daily_data(['91656'], 'id', date_min, date_max, 'NGVD29', False)
25
+
26
+ if not s65e_s.has_data():
27
+ return
28
+
29
+ df_s65e_s = s65e_s.to_dataframe(True)
30
+ df_s65e_s = df_replace_missing_with_nan(df_s65e_s) # Replace flagged 0 values and -99999.0 with NaN
31
+ df_s65e_s.reset_index(inplace=True) # Reset index so datetime is a column
32
+ df_s65e_s['value'] = df_s65e_s['value'] * (0.0283168466 * 86400) # Convert flow from cfs to cmd
33
+ df_s65e_s = df_s65e_s[['datetime', 'value']].copy() # Grab only the columns we need
34
+ df_s65e_s.rename(columns={'datetime': 'date', 'value': f'S65E_S_FLOW_cfs'}, inplace=True) # Rename columns to expected names
35
+
36
+ # S65EX1_S
37
+ s65ex1_s = api.get_daily_data(['AL760'], 'id', date_min, date_max, 'NGVD29', False)
38
+
39
+ if not s65ex1_s.has_data():
40
+ return
41
+
42
+ df_s65ex1_s = s65ex1_s.to_dataframe(True)
43
+ df_s65ex1_s = df_replace_missing_with_nan(df_s65ex1_s) # Replace flagged 0 values and -99999.0 with NaN
44
+ df_s65ex1_s.reset_index(inplace=True) # Reset index so datetime is a column
45
+ df_s65ex1_s['value'] = df_s65ex1_s['value'] * (0.0283168466 * 86400) # Convert flow from cfs to cmd
46
+ df_s65ex1_s = df_s65ex1_s[['datetime', 'value']].copy() # Grab only the columns we need
47
+ df_s65ex1_s.rename(columns={'datetime': 'date', 'value': f'S65EX1_S_FLOW_cfs'}, inplace=True) # Rename columns to expected names
48
+
49
+ # Combine the data from both stations into a single dataframe
50
+ df = pd.merge(df_s65e_s, df_s65ex1_s, on='date', how='outer', suffixes=('_S65E_S', '_S65EX1_S'))
51
+
52
+ # Reformat the data to the expected layout
53
+ df = _reformat_s65e_total_df(df)
54
+
55
+ # Write the data to a file
56
+ df.to_csv(f"{workspace}/S65E_total.csv")
57
+
64
58
 
65
59
  def _reformat_s65e_total_file(workspace: str):
66
60
  # Read in the data
67
61
  df = pd.read_csv(f"{workspace}/S65E_total.csv")
68
62
 
69
- # Drop unused columns
70
- df.drop('Unnamed: 0', axis=1, inplace=True)
63
+ # Reformat the data
64
+ df = _reformat_s65e_total_df(df)
71
65
 
66
+ # Write the updated data back to the file
67
+ df.to_csv(f"{workspace}/S65E_total.csv")
68
+
69
+
70
+ def _reformat_s65e_total_df(df: pd.DataFrame) -> pd.DataFrame:
72
71
  # Convert date column to datetime
73
72
  df['date'] = pd.to_datetime(df['date'], format='%d-%b-%Y')
74
73
 
@@ -81,8 +80,9 @@ def _reformat_s65e_total_file(workspace: str):
81
80
  # Drop rows that are missing all their values
82
81
  df.dropna(how='all', inplace=True)
83
82
 
84
- # Write the updated data back to the file
85
- df.to_csv(f"{workspace}/S65E_total.csv")
83
+ # Return the reformatted dataframe
84
+ return df
85
+
86
86
 
87
87
  if __name__ == "__main__":
88
88
  workspace = sys.argv[1].rstrip("/")
@@ -38,7 +38,7 @@ def get_bias_corrected_data(
38
38
  # Prepare the observed data by filling NaN values with the 10yr average
39
39
  prepared_od = prep_observed_data(observed_data)
40
40
  historical_data = geoglows.data.retro_daily(reach_id)
41
- # Get the historical simulation data for the given reach ID - TODO: Do we for sure want to cache the historical data?
41
+ # Get the historical simulation data for the given reach ID
42
42
  # I am reading the observed data that we queried earlier instead of caching it
43
43
  # historical_data = None
44
44
 
@@ -1,11 +1,8 @@
1
1
  import os
2
2
  import sys
3
3
  import pandas as pd
4
- import rpy2.robjects as ro
5
- from rpy2.robjects import pandas2ri
6
4
  import geoglows
7
5
  import datetime
8
- from loone_data_prep.utils import get_dbkeys
9
6
  from loone_data_prep.flow_data.forecast_bias_correction import (
10
7
  get_bias_corrected_data,
11
8
  )
@@ -63,51 +60,6 @@ FORECAST_DATE = (datetime.datetime.now()).strftime("%Y%m%d")
63
60
  GEOGLOWS_ENDPOINT = "https://geoglows.ecmwf.int/api/"
64
61
 
65
62
 
66
- def get_stations_latitude_longitude(station_ids: list[str]):
67
- """Gets the latitudes and longitudes of the given stations.
68
-
69
- Args:
70
- station_ids (list[str]): The ids of the stations to get the
71
- latitudes/longitudes of
72
-
73
- Returns:
74
- (dict[str, tuple[numpy.float64, numpy.float64]]): A dictionary of
75
- format dict<station_id:(latitude,longitude)>
76
-
77
- If a station's latitude/longitude fails to download then its station_id
78
- won't be a key in the returned dictionary.
79
- """
80
- # The dict that holds the data that gets returned
81
- station_data = {}
82
-
83
- # Get the station/dbkey data
84
- r_dataframe = get_dbkeys(
85
- station_ids=station_ids,
86
- category="SW",
87
- param="",
88
- stat="",
89
- recorder="",
90
- detail_level="full",
91
- )
92
-
93
- # Convert the r dataframe to a pandas dataframe
94
- with (ro.default_converter + pandas2ri.converter).context():
95
- pd_dataframe = ro.conversion.get_conversion().rpy2py(r_dataframe)
96
-
97
- # Filter out extra rows for each station from the dataframe
98
- pd_dataframe.drop_duplicates(subset="Station", keep="first", inplace=True)
99
-
100
- # Get latitude/longitude of each station
101
- for index in pd_dataframe.index:
102
- station = pd_dataframe["Station"][index]
103
- latitude = pd_dataframe["Latitude"][index]
104
- longitude = pd_dataframe["Longitude"][index]
105
-
106
- station_data[station] = latitude, longitude
107
-
108
- return station_data
109
-
110
-
111
63
  def get_reach_id(latitude: float, longitude: float):
112
64
  """Gets the reach id for the given latitude/longitude.
113
65
 
@@ -273,70 +225,32 @@ def _format_stats_DataFrame(dataframe: pd.core.frame.DataFrame):
273
225
  dataframe.index = dataframe.index.normalize()
274
226
 
275
227
  # Convert m^3/s data to m^3/h
276
- dataframe = dataframe.transform(lambda x: x * SECONDS_IN_HOUR)
228
+ dataframe = dataframe * SECONDS_IN_HOUR
277
229
 
278
230
  # Make negative values 0
279
231
  dataframe.clip(0, inplace=True)
280
232
 
281
- # Max Column (Max)
282
- column_max = dataframe[["flow_max"]].copy()
283
- column_max = column_max.groupby([column_max.index]).max()
284
-
285
- # 75th Percentile Column (Average)
286
- column_75percentile = dataframe[["flow_75p"]].copy()
287
- column_75percentile = column_75percentile.groupby(
288
- [column_75percentile.index]
289
- ).mean()
290
-
291
- # Average Column (Weighted Average)
292
- column_average = dataframe[["flow_avg"]].copy()
293
- column_average.transform(lambda x: x / 8)
294
- column_average = column_average.groupby([column_average.index]).sum()
295
-
296
- # 25th Percentile Column (Average)
297
- column_25percentile = dataframe[["flow_25p"]].copy()
298
- column_25percentile = column_25percentile.groupby(
299
- [column_25percentile.index]
300
- ).mean()
301
-
302
- # Min Column (Min)
303
- column_min = dataframe[["flow_min"]].copy()
304
- column_min = column_min.groupby([column_min.index]).min()
305
-
306
- # Convert values in each column from m^3/h to m^3/d
307
- column_max = column_max.transform(lambda x: x * HOURS_IN_DAY)
308
- column_75percentile = column_75percentile.transform(
309
- lambda x: x * HOURS_IN_DAY
310
- )
311
- column_average = column_average.transform(lambda x: x * HOURS_IN_DAY)
312
- column_25percentile = column_25percentile.transform(
313
- lambda x: x * HOURS_IN_DAY
233
+ grouped = dataframe.groupby(dataframe.index).mean()
234
+ # Convert from m^3/h → m^3/d
235
+ grouped = grouped * HOURS_IN_DAY
236
+
237
+ # Rename columns
238
+ grouped = grouped.rename(
239
+ columns={
240
+ "flow_max": "flow_max_m^3/d",
241
+ "flow_75p": "flow_75%_m^3/d",
242
+ "flow_avg": "flow_avg_m^3/d",
243
+ "flow_med": "flow_med_m^3/d",
244
+ "flow_25p": "flow_25%_m^3/d",
245
+ "flow_min": "flow_min_m^3/d",
246
+ }
314
247
  )
315
- column_min = column_min.transform(lambda x: x * HOURS_IN_DAY)
316
-
317
- # Append modified columns into one pandas DataFrame
318
- dataframe_result = pd.DataFrame()
319
- dataframe_result.index = dataframe.groupby([dataframe.index]).mean().index
320
- dataframe_result["flow_max_m^3/d"] = column_max["flow_max"].tolist()
321
- dataframe_result["flow_75%_m^3/d"] = column_75percentile[
322
- "flow_75p"
323
- ].tolist()
324
- dataframe_result["flow_avg_m^3/d"] = column_average[
325
- "flow_avg"
326
- ].tolist()
327
- dataframe_result["flow_25%_m^3/d"] = column_25percentile[
328
- "flow_25p"
329
- ].tolist()
330
- dataframe_result["flow_min_m^3/d"] = column_min["flow_min"].tolist()
331
248
 
332
- # Format datetimes to just dates
333
- dataframe_result.index = dataframe_result.index.strftime("%Y-%m-%d")
249
+ # Format index as date string and rename
250
+ grouped.index = grouped.index.strftime("%Y-%m-%d")
251
+ grouped.index.name = "date"
334
252
 
335
- # Rename index from datetimes to date
336
- dataframe_result.rename_axis("date", inplace=True)
337
-
338
- # Return resulting DataFrame
339
- return dataframe_result
253
+ return grouped
340
254
 
341
255
 
342
256
  def main(
@@ -45,20 +45,22 @@ def main(workspace: str, dbkeys: dict = DBKEYS) -> dict:
45
45
  Returns:
46
46
  dict: Success or error message
47
47
  """
48
+ # Make a copy of the dbkeys dictionary because key value pairs will be removed as they are successfully downloaded
49
+ dbkeys = dbkeys.copy()
48
50
 
49
51
  # Retrieve inflow data
50
52
  for dbkey, station in dbkeys.copy().items():
51
- file_name = f"{station}_FLOW_cmd.csv"
53
+ file_name = f"{station.replace(' ', '_')}_FLOW_cmd.csv"
52
54
  date_latest = find_last_date_in_csv(workspace, file_name)
53
55
 
54
56
  # File with data for this dbkey does NOT already exist (or possibly some other error occurred)
55
57
  if date_latest is None:
56
58
  # Download all the data
57
59
  print(f'Downloading all inflow data for {station}')
58
- hydro.get(workspace, dbkey)
60
+ hydro.get(workspace=workspace, dbkey=dbkey, station=station)
59
61
  else:
60
62
  # Check whether the latest data is already up to date.
61
- if dbhydro_data_is_latest(date_latest):
63
+ if dbhydro_data_is_latest(date_latest, dbkey):
62
64
  # Notify that the data is already up to date
63
65
  print(f'Downloading of new inflow data skipped for Station {station} (dbkey: {dbkey}). Data is already up to date.')
64
66
 
@@ -67,8 +69,15 @@ def main(workspace: str, dbkeys: dict = DBKEYS) -> dict:
67
69
  continue
68
70
 
69
71
  # Download only the new data
70
- print(f'Downloading new inflow data for {station} starting from date {date_latest}')
71
- hydro.get(workspace, dbkey, date_latest)
72
+ date_next = (pd.to_datetime(date_latest) + pd.Timedelta(days=1)).strftime("%Y-%m-%d")
73
+ print(f'Downloading new inflow data for {station} starting from date {date_next}')
74
+ hydro.get(workspace=workspace, dbkey=dbkey, date_min=date_next, station=station)
75
+
76
+ # Check if the station name contains a space
77
+ if ' ' in station:
78
+ # Replace space with underscore in the station name
79
+ station_previous = station
80
+ station = station.replace(' ', '_')
72
81
 
73
82
  # Make sure both our original data and newly downloaded data exist
74
83
  df_original_path = os.path.join(workspace, f"{station}_FLOW_cmd.csv")
@@ -94,7 +103,7 @@ def main(workspace: str, dbkeys: dict = DBKEYS) -> dict:
94
103
  S65E_total.get(workspace, date_max=datetime.now().strftime("%Y-%m-%d"))
95
104
  else:
96
105
  # Check whether the latest data is already up to date.
97
- if dbhydro_data_is_latest(date_latest):
106
+ if dbhydro_data_is_latest(date_latest, '91656') and dbhydro_data_is_latest(date_latest, 'AL760'):
98
107
  # Notify that the data is already up to date
99
108
  print(f'Downloading of new inflow data skipped for S65E_total. Data is already up to date.')
100
109
  else:
@@ -104,8 +113,9 @@ def main(workspace: str, dbkeys: dict = DBKEYS) -> dict:
104
113
 
105
114
  try:
106
115
  # Download only the new data
107
- print(f'Downloading new S65E_total data starting from date {date_latest}')
108
- S65E_total.get(workspace, date_min=date_latest, date_max=datetime.now().strftime("%Y-%m-%d"))
116
+ date_next = (pd.to_datetime(date_latest) + pd.Timedelta(days=1)).strftime("%Y-%m-%d")
117
+ print(f'Downloading new S65E_total data starting from date {date_next}')
118
+ S65E_total.get(workspace, date_min=date_next, date_max=datetime.now().strftime("%Y-%m-%d"))
109
119
 
110
120
  # Merge the new data with the original data
111
121
  df_original = pd.read_csv(os.path.join(workspace, original_file_name), index_col=0)
@@ -56,8 +56,8 @@ def _get_outflow_data_from_station_ids(workspace: str, station_ids: list) -> dic
56
56
  dict: Success or error message
57
57
  """
58
58
  # Get dbkeys from station ids
59
- dbkeys = list(get_dbkeys(station_ids, "SW", "FLOW", "MEAN", "PREF", detail_level="dbkey"))
60
- dbkeys.extend(list(get_dbkeys(station_ids, "SW", "FLOW", "MEAN", "DRV", detail_level="dbkey")))
59
+ dbkeys = get_dbkeys(station_ids, "SW", "FLOW", "MEAN", "PREF")
60
+ dbkeys.extend(get_dbkeys(station_ids, "SW", "FLOW", "MEAN", "DRV"))
61
61
 
62
62
  for dbkey in dbkeys:
63
63
  hydro.get(workspace, dbkey, "2000-01-01")
@@ -94,6 +94,8 @@ def main(workspace: str, dbkeys: dict = DBKEYS, station_ids: list = STATION_IDS)
94
94
  Returns:
95
95
  dict: Success or error message
96
96
  """
97
+ # Make a copy of the dbkeys dictionary because key value pairs will be removed as they are successfully downloaded
98
+ dbkeys = dbkeys.copy()
97
99
 
98
100
  # No dbkeys given, attempt to get data from station ids
99
101
  if dbkeys is None:
@@ -102,16 +104,16 @@ def main(workspace: str, dbkeys: dict = DBKEYS, station_ids: list = STATION_IDS)
102
104
  # Get outflow data from dbkeys
103
105
  for dbkey, station in dbkeys.copy().items():
104
106
  # Get the date of the latest data in the csv file (if any)
105
- date_latest = find_last_date_in_csv(workspace, f"{station}_FLOW_cmd.csv")
107
+ date_latest = find_last_date_in_csv(workspace, f"{station.replace(' ', '_')}_FLOW_cmd.csv")
106
108
 
107
109
  # File with data for this dbkey does NOT already exist (or possibly some other error occurred)
108
110
  if date_latest is None:
109
111
  # Download all data
110
112
  print(f'Downloading all outflow data for {station}')
111
- hydro.get(workspace, dbkey, "2000-01-01")
113
+ hydro.get(workspace=workspace, dbkey=dbkey, date_min="2000-01-01", station=station)
112
114
  else:
113
115
  # Check whether the latest data is already up to date.
114
- if dbhydro_data_is_latest(date_latest):
116
+ if dbhydro_data_is_latest(date_latest, dbkey):
115
117
  # Notify that the data is already up to date
116
118
  print(f'Downloading of new outflow data skipped for Station {station} (dbkey: {dbkey}). Data is already up to date.')
117
119
 
@@ -120,8 +122,15 @@ def main(workspace: str, dbkeys: dict = DBKEYS, station_ids: list = STATION_IDS)
120
122
  continue
121
123
 
122
124
  # Download only the new data
123
- print(f'Downloading new outflow data for {station} starting from date {date_latest}')
124
- hydro.get(workspace, dbkey, date_latest)
125
+ date_next = (pd.to_datetime(date_latest) + pd.Timedelta(days=1)).strftime("%Y-%m-%d")
126
+ print(f'Downloading new outflow data for {station} starting from date {date_next}')
127
+ hydro.get(workspace=workspace, dbkey=dbkey, date_min=date_next, station=station)
128
+
129
+ # Check if the station name contains a space
130
+ if ' ' in station:
131
+ # Replace space with underscore in the station name
132
+ station_previous = station
133
+ station = station.replace(' ', '_')
125
134
 
126
135
  # Make sure both our original data and newly downloaded data exist
127
136
  df_old_path = os.path.join(workspace, f"{station}_FLOW_cmd.csv")