mpcaHydro 2.0.0__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.
mpcaHydro/WISKI.py ADDED
@@ -0,0 +1,351 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Mon Jul 10 16:18:03 2023
4
+
5
+ @author: mfratki
6
+ """
7
+ import requests
8
+ import pandas as pd
9
+ import time
10
+
11
+ #TODO: Use this url to make sure web service is working https://wiskiweb01.pca.state.mn.us/
12
+ class Service():
13
+ base_url = 'http://wiskiweb01.pca.state.mn.us/KiWIS/KiWIS?'
14
+ base_dict = {
15
+ 'datasource': '0',
16
+ 'service': 'kisters',
17
+ 'type': 'queryServices',
18
+ 'format': 'json'}
19
+
20
+ def __init__(self):
21
+ #TODO: store request types in a file and load them here to avoid making a request when the class is instantiated
22
+ #url = self.url({'request': 'getrequestinfo'})
23
+ #self.requestTypes = requests.get(url).json()[0]
24
+ self._url = None
25
+ self._args = None
26
+
27
+ # def _requestTypes(self):
28
+ # url = self.url({'request': 'getrequestinfo'})
29
+ # self.requestTypes = requests.get(url).json()[0]
30
+ # self._url = None
31
+ # self._args = None
32
+ def _requestTypes(self):
33
+ url = self.url({'request': 'getrequestinfo'})
34
+ return requests.get(url).json()[0]
35
+
36
+ def getRequests(self):
37
+ return list(self._requestTypes()['Requests'].keys())
38
+
39
+ def queryfields(self,request_type):
40
+ return list(self._requestTypes()['Requests'][request_type]['QueryFields']['Content'].keys())
41
+
42
+ def returnfields(self,request_type):
43
+ return list(self._requestTypes()['Requests'][request_type]['Returnfields']['Content'].keys())
44
+
45
+ def optionalfields(self,request_type):
46
+ return list(self._requestTypes()['Requests'][request_type]['Optionalfields']['Content'].keys())
47
+
48
+ def formats(self,request_type):
49
+ return list(self._requestTypes()['Requests'][request_type]['Formats']['Content'].keys())
50
+
51
+ def info(self,request_type):
52
+ url = self.url({'request': 'getrequestinfo'})
53
+ response = requests.get(url)
54
+ get_requests = response.json()
55
+ return get_requests[0]['Requests'].keys()
56
+
57
+
58
+ def url(self,args_dict):
59
+ args_dict = self.base_dict | args_dict
60
+ args = []
61
+ for k,v in args_dict.items():
62
+ if v is None:
63
+ continue
64
+ elif isinstance(v,list):
65
+ v = [str(vv) for vv in v]
66
+ v = ','.join(v)
67
+ args.append(f'{k}={v}')
68
+ args = '&'.join(args)
69
+
70
+ url = self.base_url + args
71
+ self._url = url
72
+ return url
73
+
74
+
75
+ def df(self,args_dict):
76
+
77
+ # Download request
78
+ # print('Downloading')
79
+ response = requests.get(self.url(args_dict))
80
+
81
+ response.raise_for_status() # raises exception when not a 2xx response
82
+
83
+
84
+
85
+ if response.status_code != 200:
86
+ print('Error: ' + response.json()['message'])
87
+ return 1
88
+
89
+ get_requests = response.json()
90
+ # Convert to dataframe
91
+ if args_dict['request'] in ['getTimeseriesValues']:
92
+ dfs = []
93
+ for get_request in get_requests:
94
+ df = pd.DataFrame(get_request['data'],columns = get_request['columns'].split(','))
95
+ del get_request['data']
96
+ del get_request['rows']
97
+ del get_request['columns']
98
+ for k,v in get_request.items(): df[k] = v
99
+ dfs.append(df)
100
+ df = pd.concat(dfs)
101
+ else:
102
+ df = pd.DataFrame(get_requests[1:], columns = get_requests[0])
103
+
104
+ # print('Done!')
105
+ return df
106
+
107
+ def get(self,args):
108
+ request_type = args['request']
109
+ assert(request_type in self.getRequests())
110
+ _args = {queryfield: None for queryfield in self.queryfields(request_type)} | {optionalfield: None for optionalfield in self.optionalfields(request_type)}
111
+ args = {**_args, **args}
112
+ self._args = args
113
+ return self.df(args)
114
+
115
+ def _filter(self,args):
116
+
117
+ '''
118
+ Filter for ensuring not too many values are requested and determining the proper division
119
+ given the number of timeseries, timeseries length, and timeseries sampling interval
120
+ '''
121
+ 'minute','hour','daily'
122
+
123
+ MAX_OUTPUT = 240000 #True max output is 250,000 but giving myself a bit of a buffer
124
+
125
+
126
+ n_timeseries = 1
127
+ n_years = 1
128
+ #1 timeseries for 1 year
129
+ n_values = 60*24*365*n_timeseries*n_years
130
+
131
+ if n_values < MAX_OUTPUT :
132
+ return 0
133
+ elif n_timeseries == 1:
134
+ n_values/MAX_OUTPUT
135
+
136
+
137
+
138
+ '''
139
+ Potential use cases:
140
+
141
+ 1. timeseries for a given ts_id
142
+ 2. All timeseries for a given station
143
+ 3. All timeseries for a given parameter
144
+ 4. All timeseries for a given huc_id
145
+ 5. All timeseries of a given resolution
146
+
147
+ '''
148
+
149
+ class pyWISK():
150
+
151
+ def __init__(self):
152
+ self.service = Service()
153
+
154
+
155
+ def get(self,args_dict):
156
+ return self.service.get(args_dict)
157
+
158
+ def get_ts(self,
159
+ ts_ids = None,
160
+ huc_id = None,
161
+ station_nos = None,
162
+ parametertype_id = None,
163
+ parameter_no = None,
164
+ start_date = '1996-01-01',
165
+ end_date = '2050-12-31',
166
+ stationgroup_id = None,
167
+ timezone = 'UTC'):
168
+
169
+ if ts_ids is None:
170
+ print('Determing Timeseries IDs')
171
+ ts_ids = self.get_ts_ids(station_nos,huc_id,parametertype_id)
172
+ print('Done!')
173
+
174
+ #print('Downloading Timeseries Data')
175
+ args = {'request':'getTimeseriesValues',
176
+ 'ts_id' : ts_ids,
177
+ 'from': start_date,
178
+ 'to': end_date,
179
+ 'returnfields': ['Timestamp', 'Value', 'Quality Code','Quality Code Name'],
180
+ 'metadata': 'true',
181
+ 'md_returnfields': ['ts_unitsymbol',
182
+ 'ts_name',
183
+ 'ts_id',
184
+ 'station_no',
185
+ 'station_name',
186
+ 'station_latitude',
187
+ 'station_longitude',
188
+ 'parametertype_id',
189
+ 'parametertype_name',
190
+ 'stationparameter_no',
191
+ 'stationparameter_name'],
192
+ 'timezone':timezone,
193
+ 'ca_sta_returnfields': ['stn_HUC12','stn_EQuIS_ID']}
194
+
195
+ df = self.service.get(args)
196
+ #print('Done!')
197
+ return df
198
+
199
+ def get_stations(self,
200
+ huc_id = None,
201
+ parametertype_id = None,
202
+ stationgroup_id = None,
203
+ stationparameter_no = None,
204
+ station_no = None,
205
+ returnfields = []):
206
+
207
+ args = {'request':'getStationList'}
208
+
209
+ returnfields = list(set(['ca_sta','station_no','station_name'] + returnfields))
210
+
211
+ args ={'request': 'getStationList',
212
+ 'stationparameter_no': stationparameter_no,
213
+ 'stationgroup_id': stationgroup_id,
214
+ 'parametertype_id': parametertype_id,
215
+ 'station_no': station_no,
216
+ #'object_type': object_type,
217
+ 'returnfields': returnfields,
218
+ # 'parametertype_id','parametertype_name',
219
+ # 'station_latitude','station_longitude',
220
+ # 'stationparameter_no','stationparameter_name'],
221
+ 'ca_sta_returnfields': ['stn_HUC12','stn_EQuIS_ID','stn_AUID','hydrounit_title','hydrounit_no','NearestTown']
222
+ }
223
+
224
+
225
+ df = self.service.get(args)
226
+ if huc_id is not None: df = df.loc[df['stn_HUC12'].str.startswith(huc_id)]
227
+ return df
228
+
229
+ def get_ts_ids(self,
230
+ station_nos=None,
231
+ huc_id = None,
232
+ parametertype_id = None,
233
+ stationparameter_no = None,
234
+ stationgroup_id = None,
235
+ ts_name = None,
236
+ returnfields = None):
237
+
238
+ if station_nos is None:
239
+ station_nos = self.get_stations(huc_id,parametertype_id,stationgroup_id,stationparameter_no)['station_no'].to_list()
240
+
241
+ if returnfields is None:
242
+ returnfields = ['ts_id','ts_name','ca_sta','station_no',
243
+ 'ts_unitsymbol',
244
+ 'parametertype_id','parametertype_name',
245
+ 'station_latitude','station_longitude',
246
+ 'stationparameter_no','stationparameter_name',
247
+ 'station_no','station_name',
248
+ 'coverage','ts_density']
249
+
250
+
251
+ args ={'request': 'getTimeseriesList',
252
+ 'station_no': station_nos,
253
+ 'parametertype_id': parametertype_id,
254
+ 'stationparameter_no': stationparameter_no,
255
+ 'ts_name' : ts_name,
256
+ 'returnfields': returnfields,
257
+ 'ca_sta_returnfields': ['stn_HUC12','stn_EQuIS_ID','stn_AUID']}
258
+
259
+ df = self.service.get(args)
260
+ return df
261
+
262
+
263
+
264
+ def get_wplmn(self,station_nos):
265
+
266
+ PARAMETERS_MAP={'5004':'TP Load',
267
+ '5005':'TP Conc',
268
+ '5014':'TSS Load',
269
+ '5015':'TSS Conc',
270
+ '5024':'N Load',
271
+ '5025':'N Conc',
272
+ '5034':'OP Load',
273
+ '5035':'OP Conc',
274
+ '5044':'TKN Load',
275
+ '5045':'TKN Conc',
276
+ '262' :'Flow'}
277
+
278
+ ts_ids = self.get_ts_ids(station_nos = station_nos,
279
+ stationgroup_id = '1319204',
280
+ stationparameter_no = list(PARAMETERS_MAP.keys()),
281
+ ts_name = ['20.Day.Mean'])
282
+
283
+ if len(ts_ids) == 0:
284
+ print('No WPLMN Sites Available')
285
+ return pd.DataFrame()
286
+
287
+ dfs = []
288
+ for ts_id in ts_ids['ts_id']:
289
+ dfs.append(self.get_ts(ts_id))
290
+ time.sleep(1)
291
+
292
+ return pd.concat(dfs)
293
+
294
+
295
+ # CONSTITUENT_NAME_NO = {'Q' :['262'],#,'263'],
296
+ # 'WT' :['450'],# , '451' , '450.42','451.42'],
297
+ # 'OP' :['863' ,'5034' ,'5035'],
298
+ # 'DO' :['865' ,'866' , '867'],
299
+ # 'TP' :['5005' ,'5004'],
300
+ # 'TSS':['5014' ,'5015'],
301
+ # 'N' :['5024' ,'5025'],
302
+ # 'TKN':['5044' ,'5045']}
303
+
304
+ # TS_NAME_SELECTOR = {'Q':{'daily':['20.Day.Mean.Archive','20.Day.Mean'],
305
+ # 'unit': ['15.Rated','08.Provisional.Edited']},
306
+ # 'WT':{'daily':['20.Day.Mean','20.Day.Mean'],
307
+ # 'unit': ['09.Archive','08.Provisional.Edited']},
308
+ # 'TSS':{'daily':['20.Day.Mean','20.Day.Mean'],
309
+ # 'unit': ['09.Archive','08.Provisional.Edited']},
310
+ # 'N':{'daily':['20.Day.Mean','20.Day.Mean'],
311
+ # 'unit': ['09.Archive','08.Provisional.Edited']},
312
+ # 'TKN':{'daily':['20.Day.Mean','20.Day.Mean'],
313
+ # 'unit': ['09.Archive','08.Provisional.Edited']},
314
+ # 'TP':{'daily':['20.Day.Mean','20.Day.Mean'],
315
+ # 'unit': ['09.Archive','08.Provisional.Edited']},
316
+ # 'OP':{'daily':['20.Day.Mean','20.Day.Mean'],
317
+ # 'unit': ['09.Archive','08.Provisional.Edited']},
318
+ # 'DO':{'daily':['20.Day.Mean','20.Day.Mean'],
319
+ # 'unit': ['09.Archive','08.Provisional.Edited']}}
320
+
321
+ # def extract(self,station_nos,constituent,resolution):
322
+ # ts_names = self.TS_NAME_SELECTOR[constituent][resolution]
323
+ # data = self.get_ts_ids(station_no = station_nos,stationparameter_no = self.CONSTITUENT_NAME_NO[constituent],ts_name =ts_names)
324
+ # # Filter by MPCA distinction between internal and external sites and how time series are named
325
+ # ts_ids = pd.concat([data.loc[(data['station_no'].str.startswith('E')) & (data['ts_name'] == ts_names[1])],
326
+ # data.loc[(~data['station_no'].str.startswith('E')) & (data['ts_name'] == ts_names[0])]])
327
+ # dfs = [self.get_ts(ts_ids = ts_id) for ts_id in ts_ids['ts_id']]
328
+ # data = pd.concat(dfs)
329
+ # return data
330
+
331
+
332
+
333
+ # nutrient
334
+ # -N03N02
335
+ # -OP
336
+ # -NH3
337
+ # -TP
338
+ # -DO
339
+ # -CHla
340
+ # temperature
341
+ # flow
342
+
343
+ # test = pyWISK()
344
+
345
+ # df = test.get_ts(ts_ids = 424663010)
346
+
347
+ # df = test.get_ts(station_nos = 'W25060001')
348
+
349
+ # df = test.get_wplmn(huc8_id = '07020005')
350
+
351
+ # df = test.get_ts(huc_id = '07010205',stationgroup_id = '1319204',parametertype_id = 11500)
mpcaHydro/__init__.py ADDED
File without changes
@@ -0,0 +1,321 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Fri Jun 3 10:01:14 2022
4
+
5
+ @author: mfratki
6
+ """
7
+
8
+ import pandas as pd
9
+ #from abc import abstractmethod
10
+ from pathlib import Path
11
+ from mpcaHydro import etlWISKI, etlSWD#, etlEQUIS
12
+
13
+
14
+ #
15
+ '''
16
+ Q
17
+ WT
18
+ TSS
19
+ N
20
+ TKN
21
+ OP
22
+ TP
23
+ CHLA
24
+ DO
25
+
26
+
27
+ class Station
28
+
29
+ - id
30
+ - name
31
+ - source
32
+ - data
33
+
34
+
35
+
36
+
37
+
38
+ '''
39
+ WISKI_EQUIS_XREF = pd.read_csv(Path(__file__).parent.parent/'WISKI_EQUIS_XREF.csv')
40
+ #WISKI_EQUIS_XREF = pd.read_csv('C:/Users/mfratki/Documents/GitHub/hspf_tools/WISKI_EQUIS_XREF.csv')
41
+
42
+ AGG_DEFAULTS = {'cfs':'mean',
43
+ 'mg/l':'mean',
44
+ 'degF': 'mean',
45
+ 'lb':'sum'}
46
+
47
+ UNIT_DEFAULTS = {'Q': 'cfs',
48
+ 'TSS': 'mg/l',
49
+ 'TP' : 'mg/l',
50
+ 'OP' : 'mg/l',
51
+ 'TKN': 'mg/l',
52
+ 'N' : 'mg/l',
53
+ 'WT' : 'degF',
54
+ 'WL' : 'ft'}
55
+
56
+ # VALID_UNITS = {'Q': 'cfs',
57
+ # 'TSS': 'mg/l','lb',
58
+ # 'TP' : 'mg/l',
59
+ # 'OP' : 'mg/l',
60
+ # 'TKN': 'mg/l',
61
+ # 'N' : 'mg/l',
62
+ # 'WT' : 'degF',
63
+ # 'WL' : 'ft'}
64
+
65
+
66
+ def are_lists_identical(nested_list):
67
+ # Sort each sublist
68
+ sorted_sublists = [sorted(sublist) for sublist in nested_list]
69
+ # Compare all sublists to the first one
70
+ return all(sublist == sorted_sublists[0] for sublist in sorted_sublists)
71
+
72
+ class dataManager():
73
+
74
+ def __init__(self,folderpath):
75
+
76
+ self.data = {}
77
+ self.folderpath = Path(folderpath)
78
+
79
+ def get_wiski_stations(self):
80
+ return list(WISKI_EQUIS_XREF['WISKI_STATION_NO'].unique())
81
+
82
+ def get_equis_stations(self):
83
+ return list(WISKI_EQUIS_XREF['EQUIS_STATION_ID'].unique())
84
+
85
+ def wiski_equis_alias(self,wiski_station_id):
86
+ equis_ids = list(set(WISKI_EQUIS_XREF.loc[WISKI_EQUIS_XREF['WISKI_STATION_NO'] == wiski_station_id,'WISKI_EQUIS_ID'].to_list()))
87
+ equis_ids = [equis_id for equis_id in equis_ids if not pd.isna(equis_id)]
88
+ if len(equis_ids) == 0:
89
+ return []
90
+ elif len(equis_ids) > 1:
91
+ print(f'Too Many Equis Stations for {wiski_station_id}')
92
+ raise
93
+ else:
94
+ return equis_ids[0]
95
+
96
+ def wiski_equis_associations(self,wiski_station_id):
97
+ equis_ids = list(WISKI_EQUIS_XREF.loc[WISKI_EQUIS_XREF['WISKI_STATION_NO'] == wiski_station_id,'EQUIS_STATION_ID'].unique())
98
+ equis_ids = [equis_id for equis_id in equis_ids if not pd.isna(equis_id)]
99
+ if len(equis_ids) == 0:
100
+ return []
101
+ else:
102
+ return equis_ids
103
+
104
+ def equis_wiski_associations(self,equis_station_id):
105
+ wiski_ids = list(WISKI_EQUIS_XREF.loc[WISKI_EQUIS_XREF['EQUIS_STATION_ID'] == equis_station_id,'WISKI_STATION_NO'].unique())
106
+ wiski_ids = [wiski_id for wiski_id in wiski_ids if not pd.isna(wiski_id)]
107
+ if len(wiski_ids) == 0:
108
+ return []
109
+ else:
110
+ return wiski_ids
111
+
112
+ def _equis_wiski_associations(self,equis_station_ids):
113
+ wiski_stations = [self.equis_wiski_associations(equis_station_id) for equis_station_id in equis_station_ids]
114
+ if are_lists_identical(wiski_stations):
115
+ return wiski_stations[0]
116
+ else:
117
+ return []
118
+
119
+ def _download_station_data(self,station_id,station_origin,overwrite=False):
120
+ assert(station_origin in ['wiski','equis','swd','wplmn'])
121
+ if station_origin == 'wiski':
122
+ #equis_stations = list(WISKI_EQUIS_XREF.loc[WISKI_EQUIS_XREF['WISKI_STATION_NO'] == station_id,'WISKI_EQUIS_ID'].unique())
123
+ #[self.download_station_data(equis_station,'equis',overwrite = overwrite) for equis_station in equis_stations]
124
+ self.download_station_data(station_id,'wiski',overwrite = overwrite)
125
+ equis_alias = self.wiski_equis_alias(station_id)
126
+ self.download_station_data(equis_alias,'swd',overwrite = overwrite)
127
+ elif station_origin == 'wplmn':
128
+ self.download_station_data(station_id,'wplmn',overwrite = overwrite)
129
+ equis_alias = self.wiski_equis_alias(station_id)
130
+ self.download_station_data(equis_alias,'swd',overwrite = overwrite)
131
+ else:
132
+ wiski_station = self.equis_wiski_associations(station_id)
133
+ #wiski_station = WISKI_EQUIS_XREF.loc[WISKI_EQUIS_XREF['EQUIS_STATION_ID'] == station_id,'WISKI_STATION_NO']
134
+ self.download_station_data(station_id,'equis',overwrite = overwrite)
135
+ self.download_station_data(wiski_station,'wiski',overwrite = overwrite)
136
+
137
+
138
+ def download_station_data(self,station_id,source,folderpath=None,overwrite = False):
139
+ assert(source in ['wiski','equis','swd','wplmn'])
140
+ station_id = str(station_id)
141
+ save_name = station_id
142
+ if source == 'wplmn':
143
+ save_name = station_id + '_wplmn'
144
+
145
+ if folderpath is None:
146
+ folderpath = self.folderpath
147
+ else:
148
+ folderpath = Path(folderpath)
149
+
150
+
151
+ if (folderpath.joinpath(save_name + '.csv').exists()) & (not overwrite):
152
+ print (f'{station_id} data already downloaded')
153
+ return
154
+
155
+ if source == 'wiski':
156
+ data = etlWISKI.download(station_id)
157
+ elif source == 'swd':
158
+ data = etlSWD.download(station_id)
159
+ elif source == 'equis':
160
+ data = etlSWD.download(station_id)
161
+ else:
162
+ data = etlWISKI.download(station_id,wplmn=True)
163
+ #raise NotImplementedError()
164
+ #data = etlEQUIS.download(station_id)
165
+
166
+
167
+
168
+ if len(data) > 0:
169
+ data.to_csv(folderpath.joinpath(save_name + '.csv'))
170
+ self.data[station_id] = data
171
+ else:
172
+ print(f'No {source} calibration cata available at Station {station_id}')
173
+
174
+
175
+ def _load(self,station_id):
176
+ df = pd.read_csv(self.folderpath.joinpath(station_id + '.csv'),
177
+ index_col='datetime',
178
+ parse_dates=['datetime'],
179
+ #usecols=['Ts Date','Station number','variable', 'value','reach_id'],
180
+ dtype={'station_id': str, 'value': float, 'variable': str,'constituent':str,'unit':str})
181
+ self.data[station_id] = df
182
+ return df
183
+
184
+ def load(self,station_id):
185
+ try:
186
+ df = self.data[station_id]
187
+ except:
188
+ df = pd.read_csv(self.folderpath.joinpath(station_id + '.csv'),
189
+ index_col='datetime',
190
+ parse_dates=['datetime'],
191
+ #usecols=['Ts Date','Station number','variable', 'value','reach_id'],
192
+ dtype={'station_id': str, 'value': float, 'variable': str,'constituent':str,'unit':str})
193
+ self.data[station_id] = df
194
+ return df
195
+
196
+ def info(self,constituent):
197
+ return pd.concat([self._load(file.stem) for file in self.folderpath.iterdir() if file.suffix == '.csv'])[['station_id','constituent','value']].groupby(by = ['station_id','constituent']).count()
198
+
199
+ def get_wplmn_data(self,station_id,constituent,unit = 'mg/l', agg_period = 'YE', samples_only = True):
200
+
201
+ assert constituent in ['Q','TSS','TP','OP','TKN','N','WT','DO','WL','CHLA']
202
+ station_id = station_id + '_wplmn'
203
+ dfsub = self._load(station_id)
204
+
205
+ if samples_only:
206
+ dfsub = dfsub.loc[dfsub['quality_id'] == 3]
207
+ agg_func = 'mean'
208
+
209
+ dfsub = dfsub.loc[(dfsub['constituent'] == constituent) &
210
+ (dfsub['unit'] == unit),
211
+ ['value','data_format','source']]
212
+
213
+
214
+ df = dfsub[['value']].resample(agg_period).agg(agg_func)
215
+
216
+ if df.empty:
217
+ dfsub = df
218
+ else:
219
+
220
+ df['data_format'] = dfsub['data_format'].iloc[0]
221
+ df['source'] = dfsub['source'].iloc[0]
222
+
223
+ #if (constituent == 'TSS') & (unit == 'lb'): #convert TSS from lbs to us tons
224
+ # dfsub['value'] = dfsub['value']/2000
225
+
226
+ #dfsub = dfsub.resample('H').mean().dropna()
227
+
228
+ df.attrs['unit'] = unit
229
+ df.attrs['constituent'] = constituent
230
+ return df['value'].to_frame().dropna()
231
+
232
+ def get_data(self,station_id,constituent,agg_period = 'D'):
233
+ return self._get_data([station_id],constituent,agg_period)
234
+
235
+ def _get_data(self,station_ids,constituent,agg_period = 'D'):
236
+ '''
237
+
238
+ Returns the processed observational data associated with the calibration specific id.
239
+
240
+
241
+ Parameters
242
+ ----------
243
+ station_id : STR
244
+ Station ID as a string
245
+ constituent : TYPE
246
+ Constituent abbreviation used for calibration. Valid options:
247
+ 'Q',
248
+ 'TSS',
249
+ 'TP',
250
+ 'OP',
251
+ 'TKN',
252
+ 'N',
253
+ 'WT',
254
+ 'DO',
255
+ 'WL']
256
+ unit : TYPE, optional
257
+ Units of data. The default is 'mg/l'.
258
+ sample_flag : TYPE, optional
259
+ For WPLMN data this flag determines modeled loads are returned. The default is False.
260
+
261
+ Returns
262
+ -------
263
+ dfsub : Pands.Series
264
+ Pandas series of data. Note that no metadata is returned.
265
+
266
+ '''
267
+
268
+ assert constituent in ['Q','TSS','TP','OP','TKN','N','WT','DO','WL','CHLA']
269
+
270
+ unit = UNIT_DEFAULTS[constituent]
271
+ agg_func = AGG_DEFAULTS[unit]
272
+
273
+ dfsub = pd.concat([self.load(station_id) for station_id in station_ids]) # Check cache
274
+ dfsub = dfsub.loc[(dfsub['constituent'] == constituent) &
275
+ (dfsub['unit'] == unit),
276
+ ['value','data_format','source']]
277
+
278
+ df = dfsub[['value']].resample(agg_period).agg(agg_func)
279
+ df.attrs['unit'] = unit
280
+ df.attrs['constituent'] = constituent
281
+
282
+ if df.empty:
283
+
284
+ return df
285
+ else:
286
+
287
+ df['data_format'] = dfsub['data_format'].iloc[0]
288
+ df['source'] = dfsub['source'].iloc[0]
289
+
290
+
291
+ return df['value'].to_frame().dropna()
292
+
293
+
294
+ def validate_constituent(constituent):
295
+ assert constituent in ['Q','TSS','TP','OP','TKN','N','WT','DO','WL','CHLA']
296
+
297
+ def validate_unit(unit):
298
+ assert(unit in ['mg/l','lb','cfs','degF'])
299
+
300
+
301
+
302
+ # class database():
303
+ # def __init__(self,db_path):
304
+ # self.dbm = MonitoringDatabase(db_path)
305
+
306
+
307
+ # def get_timeseries(self,station_ds, constituent,agg_period):
308
+ # validate_constituent(constituent)
309
+ # unit = UNIT_DEFAULTS[constituent]
310
+ # agg_func = AGG_DEFAULTS[unit]
311
+ # return odm.get_timeseries(station_id,constituent)
312
+
313
+
314
+ # def get_samples(self,station_ds, constituent,agg_period):
315
+ # validate_constituent(constituent)
316
+ # unit = UNIT_DEFAULTS[constituent]
317
+ # agg_func = AGG_DEFAULTS[unit]
318
+ # return odm.get_sample(station_id,constituent)
319
+
320
+ # def get_samples_and_timeseries(self,station_ds, constituent,agg_period)
321
+