cwms-python 0.3.0__tar.gz → 0.4.4__tar.gz
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.
- {cwms_python-0.3.0 → cwms_python-0.4.4}/PKG-INFO +2 -2
- {cwms_python-0.3.0 → cwms_python-0.4.4}/README.md +1 -1
- cwms_python-0.4.4/cwms/__init__.py +22 -0
- {cwms_python-0.3.0 → cwms_python-0.4.4}/cwms/api.py +57 -15
- cwms_python-0.4.4/cwms/catalog/catalog.py +128 -0
- cwms_python-0.4.4/cwms/forecast/forecast_instance.py +208 -0
- cwms_python-0.4.4/cwms/forecast/forecast_spec.py +181 -0
- cwms_python-0.4.4/cwms/levels/location_levels.py +221 -0
- cwms_python-0.4.4/cwms/levels/specified_levels.py +126 -0
- cwms_python-0.4.4/cwms/locations/physical_locations.py +157 -0
- cwms_python-0.4.4/cwms/ratings/ratings.py +378 -0
- cwms_python-0.4.4/cwms/ratings/ratings_spec.py +154 -0
- cwms_python-0.4.4/cwms/ratings/ratings_template.py +148 -0
- cwms_python-0.4.4/cwms/standard_text/standard_text.py +201 -0
- cwms_python-0.4.4/cwms/timeseries/timerseries_identifier.py +135 -0
- {cwms_python-0.3.0 → cwms_python-0.4.4}/cwms/timeseries/timeseries.py +81 -16
- {cwms_python-0.3.0 → cwms_python-0.4.4}/cwms/timeseries/timeseries_bin.py +0 -7
- {cwms_python-0.3.0 → cwms_python-0.4.4}/cwms/timeseries/timeseries_txt.py +0 -167
- {cwms_python-0.3.0 → cwms_python-0.4.4}/cwms/types.py +27 -7
- {cwms_python-0.3.0 → cwms_python-0.4.4}/pyproject.toml +1 -1
- cwms_python-0.3.0/cwms/__init__.py +0 -13
- cwms_python-0.3.0/cwms/_constants.py +0 -33
- cwms_python-0.3.0/cwms/core.py +0 -26
- cwms_python-0.3.0/cwms/exceptions.py +0 -131
- cwms_python-0.3.0/cwms/forecast/forecast_instance.py +0 -260
- cwms_python-0.3.0/cwms/forecast/forecast_spec.py +0 -227
- cwms_python-0.3.0/cwms/levels/location_levels.py +0 -484
- cwms_python-0.3.0/cwms/locations/physical_locations.py +0 -47
- cwms_python-0.3.0/cwms/utils.py +0 -85
- {cwms_python-0.3.0 → cwms_python-0.4.4}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cwms-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: Corps water managerment systems (CWMS) REST API for Data Retrieval of USACE water data
|
|
5
5
|
License: LICENSE
|
|
6
6
|
Keywords: USACE,water data
|
|
@@ -49,7 +49,7 @@ from datetime import datetime, timedelta
|
|
|
49
49
|
|
|
50
50
|
end = datetime.now()
|
|
51
51
|
begin = end - timedelta(days = 10)
|
|
52
|
-
data = cwms.get_timeseries(
|
|
52
|
+
data = cwms.get_timeseries(ts_id_='Some.Fully.Qualified.Ts.Id',office_id='OFFICE1' , begin = begin, end = end)
|
|
53
53
|
|
|
54
54
|
#a cwms data object will be provided this object containes both the JSON as well
|
|
55
55
|
#as the values converted into a dataframe
|
|
@@ -28,7 +28,7 @@ from datetime import datetime, timedelta
|
|
|
28
28
|
|
|
29
29
|
end = datetime.now()
|
|
30
30
|
begin = end - timedelta(days = 10)
|
|
31
|
-
data = cwms.get_timeseries(
|
|
31
|
+
data = cwms.get_timeseries(ts_id_='Some.Fully.Qualified.Ts.Id',office_id='OFFICE1' , begin = begin, end = end)
|
|
32
32
|
|
|
33
33
|
#a cwms data object will be provided this object containes both the JSON as well
|
|
34
34
|
#as the values converted into a dataframe
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
2
|
+
|
|
3
|
+
from .api import *
|
|
4
|
+
from .catalog.catalog import *
|
|
5
|
+
from .forecast.forecast_instance import *
|
|
6
|
+
from .forecast.forecast_spec import *
|
|
7
|
+
from .levels.location_levels import *
|
|
8
|
+
from .levels.specified_levels import *
|
|
9
|
+
from .locations.physical_locations import *
|
|
10
|
+
from .ratings.ratings import *
|
|
11
|
+
from .ratings.ratings_spec import *
|
|
12
|
+
from .ratings.ratings_template import *
|
|
13
|
+
from .standard_text.standard_text import *
|
|
14
|
+
from .timeseries.timerseries_identifier import *
|
|
15
|
+
from .timeseries.timeseries import *
|
|
16
|
+
from .timeseries.timeseries_bin import *
|
|
17
|
+
from .timeseries.timeseries_txt import *
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
__version__ = version("cwms-python")
|
|
21
|
+
except PackageNotFoundError:
|
|
22
|
+
__version__ = "version-unknown"
|
|
@@ -29,7 +29,7 @@ the error.
|
|
|
29
29
|
import json
|
|
30
30
|
import logging
|
|
31
31
|
from json import JSONDecodeError
|
|
32
|
-
from typing import Optional, cast
|
|
32
|
+
from typing import Any, Optional, cast
|
|
33
33
|
|
|
34
34
|
from requests import Response
|
|
35
35
|
from requests_toolbelt import sessions # type: ignore
|
|
@@ -150,12 +150,51 @@ def api_version_text(api_version: int) -> str:
|
|
|
150
150
|
version = "application/json"
|
|
151
151
|
elif api_version == 2:
|
|
152
152
|
version = "application/json;version=2"
|
|
153
|
+
elif api_version == 102:
|
|
154
|
+
version = "application/xml;version=2"
|
|
153
155
|
else:
|
|
154
156
|
raise InvalidVersion(f"API version {api_version} is not supported.")
|
|
155
157
|
|
|
156
158
|
return version
|
|
157
159
|
|
|
158
160
|
|
|
161
|
+
def get_xml(
|
|
162
|
+
endpoint: str,
|
|
163
|
+
params: Optional[RequestParams] = None,
|
|
164
|
+
*,
|
|
165
|
+
api_version: int = API_VERSION,
|
|
166
|
+
) -> Any:
|
|
167
|
+
"""Make a GET request to the CWMS Data API.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
endpoint: The CDA endpoint for the record(s).
|
|
171
|
+
params (optional): Query parameters for the request.
|
|
172
|
+
|
|
173
|
+
Keyword Args:
|
|
174
|
+
api_version (optional): The CDA version to use for the request. If not specified,
|
|
175
|
+
the default API_VERSION will be used.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
The deserialized JSON response data.
|
|
179
|
+
|
|
180
|
+
Raises:
|
|
181
|
+
ApiError: If an error response is return by the API.
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
headers = {"Accept": api_version_text(api_version)}
|
|
185
|
+
response = SESSION.get(endpoint, params=params, headers=headers)
|
|
186
|
+
|
|
187
|
+
if response.status_code < 200 or response.status_code >= 300:
|
|
188
|
+
logging.error(f"CDA Error: response={response}")
|
|
189
|
+
raise ApiError(response)
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
return response.content.decode("utf-8")
|
|
193
|
+
except JSONDecodeError as error:
|
|
194
|
+
logging.error(f"Error decoding CDA response as xml: {error}")
|
|
195
|
+
return {}
|
|
196
|
+
|
|
197
|
+
|
|
159
198
|
def get(
|
|
160
199
|
endpoint: str,
|
|
161
200
|
params: Optional[RequestParams] = None,
|
|
@@ -182,20 +221,20 @@ def get(
|
|
|
182
221
|
headers = {"Accept": api_version_text(api_version)}
|
|
183
222
|
response = SESSION.get(endpoint, params=params, headers=headers)
|
|
184
223
|
|
|
185
|
-
if response.status_code
|
|
224
|
+
if response.status_code < 200 or response.status_code >= 300:
|
|
186
225
|
logging.error(f"CDA Error: response={response}")
|
|
187
226
|
raise ApiError(response)
|
|
188
227
|
|
|
189
228
|
try:
|
|
190
229
|
return cast(JSON, response.json())
|
|
191
230
|
except JSONDecodeError as error:
|
|
192
|
-
logging.error(f"Error decoding CDA response: {error}")
|
|
231
|
+
logging.error(f"Error decoding CDA response as json: {error}")
|
|
193
232
|
return {}
|
|
194
233
|
|
|
195
234
|
|
|
196
235
|
def post(
|
|
197
236
|
endpoint: str,
|
|
198
|
-
data:
|
|
237
|
+
data: Any,
|
|
199
238
|
params: Optional[RequestParams] = None,
|
|
200
239
|
*,
|
|
201
240
|
api_version: int = API_VERSION,
|
|
@@ -221,18 +260,19 @@ def post(
|
|
|
221
260
|
# post requires different headers than get for
|
|
222
261
|
headers = {"accept": "*/*", "Content-Type": api_version_text(api_version)}
|
|
223
262
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
263
|
+
if isinstance(data, dict):
|
|
264
|
+
data = json.dumps(data)
|
|
265
|
+
|
|
266
|
+
response = SESSION.post(endpoint, params=params, headers=headers, data=data)
|
|
227
267
|
|
|
228
|
-
if response.status_code
|
|
268
|
+
if response.status_code < 200 or response.status_code >= 300:
|
|
229
269
|
logging.error(f"CDA Error: response={response}")
|
|
230
270
|
raise ApiError(response)
|
|
231
271
|
|
|
232
272
|
|
|
233
273
|
def patch(
|
|
234
274
|
endpoint: str,
|
|
235
|
-
data:
|
|
275
|
+
data: Optional[Any] = None,
|
|
236
276
|
params: Optional[RequestParams] = None,
|
|
237
277
|
*,
|
|
238
278
|
api_version: int = API_VERSION,
|
|
@@ -256,12 +296,14 @@ def patch(
|
|
|
256
296
|
"""
|
|
257
297
|
|
|
258
298
|
headers = {"accept": "*/*", "Content-Type": api_version_text(api_version)}
|
|
299
|
+
if data is None:
|
|
300
|
+
response = SESSION.patch(endpoint, params=params, headers=headers)
|
|
301
|
+
else:
|
|
302
|
+
if isinstance(data, dict):
|
|
303
|
+
data = json.dumps(data)
|
|
304
|
+
response = SESSION.patch(endpoint, params=params, headers=headers, data=data)
|
|
259
305
|
|
|
260
|
-
response
|
|
261
|
-
endpoint, params=params, headers=headers, data=json.dumps(data)
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
if response.status_code != 200:
|
|
306
|
+
if response.status_code < 200 or response.status_code >= 300:
|
|
265
307
|
logging.error(f"CDA Error: response={response}")
|
|
266
308
|
raise ApiError(response)
|
|
267
309
|
|
|
@@ -289,6 +331,6 @@ def delete(
|
|
|
289
331
|
headers = {"Accept": api_version_text(api_version)}
|
|
290
332
|
response = SESSION.delete(endpoint, params=params, headers=headers)
|
|
291
333
|
|
|
292
|
-
if response.status_code
|
|
334
|
+
if response.status_code < 200 or response.status_code >= 300:
|
|
293
335
|
logging.error(f"CDA Error: response={response}")
|
|
294
336
|
raise ApiError(response)
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import cwms.api as api
|
|
4
|
+
from cwms.types import Data
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_locations_catalog(
|
|
8
|
+
office_id: str,
|
|
9
|
+
page: Optional[str] = None,
|
|
10
|
+
page_size: Optional[int] = 5000,
|
|
11
|
+
unit_system: Optional[str] = None,
|
|
12
|
+
like: Optional[str] = None,
|
|
13
|
+
location_category_like: Optional[str] = None,
|
|
14
|
+
location_group_like: Optional[str] = None,
|
|
15
|
+
bounding_office_like: Optional[str] = None,
|
|
16
|
+
location_kind_like: Optional[str] = None,
|
|
17
|
+
) -> Data:
|
|
18
|
+
"""Retrieves filters for a locations catalog
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
page: string
|
|
23
|
+
The endpoint used to identify where the request is located.
|
|
24
|
+
page_size: integer
|
|
25
|
+
The entries per page returned. The default value is 5000.
|
|
26
|
+
unit_system: string
|
|
27
|
+
The unit system desired in response. Valid values for this
|
|
28
|
+
field are:
|
|
29
|
+
1. SI
|
|
30
|
+
2. EN
|
|
31
|
+
office_id: string
|
|
32
|
+
The owning office of the timeseries group.
|
|
33
|
+
like: string
|
|
34
|
+
The regex for matching against the id
|
|
35
|
+
location_category_like: string
|
|
36
|
+
The regex for matching against the location category id
|
|
37
|
+
location_group_like: string
|
|
38
|
+
The regex for matching against the location group id
|
|
39
|
+
bounding_office_like: string
|
|
40
|
+
The regex for matching against the location bounding office
|
|
41
|
+
location_kind_like: string
|
|
42
|
+
Posix regular expression matching against the location kind. The location-kind is typically unset or one of the following: {"SITE", "EMBANKMENT", "OVERFLOW", "TURBINE", "STREAM", "PROJECT", "STREAMGAGE", "BASIN", "OUTLET", "LOCK", "GATE"}. Multiple kinds can be matched by using Regular Expression OR clauses. For example: "(SITE|STREAM)"
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
cwms data type
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
# CHECKS
|
|
50
|
+
if office_id is None:
|
|
51
|
+
raise ValueError("Retrieve locations catalog requires an office")
|
|
52
|
+
|
|
53
|
+
dataset = "LOCATIONS"
|
|
54
|
+
endpoint = f"catalog/{dataset}"
|
|
55
|
+
params = {
|
|
56
|
+
"page": page,
|
|
57
|
+
"page-size": page_size,
|
|
58
|
+
"units": unit_system,
|
|
59
|
+
"office": office_id,
|
|
60
|
+
"like": like,
|
|
61
|
+
"location-category-like": location_category_like,
|
|
62
|
+
"location-group-like": location_group_like,
|
|
63
|
+
"bounding-office-like": bounding_office_like,
|
|
64
|
+
"location-kind-like": location_kind_like,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
response = api.get(endpoint=endpoint, params=params, api_version=2)
|
|
68
|
+
return Data(response, selector="entries")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_timeseries_catalog(
|
|
72
|
+
office_id: str,
|
|
73
|
+
page: Optional[str] = None,
|
|
74
|
+
page_size: Optional[int] = 5000,
|
|
75
|
+
unit_system: Optional[str] = None,
|
|
76
|
+
like: Optional[str] = None,
|
|
77
|
+
timeseries_category_like: Optional[str] = None,
|
|
78
|
+
timeseries_group_like: Optional[str] = "DMZ Include List",
|
|
79
|
+
bounding_office_like: Optional[str] = None,
|
|
80
|
+
) -> Data:
|
|
81
|
+
"""Retrieves filters for the timeseries catalog
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
page: string
|
|
86
|
+
The endpoint used to identify where the request is located.
|
|
87
|
+
page_size: integer
|
|
88
|
+
The entries per page returned. The default value is 500.
|
|
89
|
+
unit_system: string
|
|
90
|
+
The unit system desired in response. Valid values for this
|
|
91
|
+
field are:
|
|
92
|
+
1. SI
|
|
93
|
+
2. EN
|
|
94
|
+
office_id: string
|
|
95
|
+
The owning office of the timeseries group.
|
|
96
|
+
like: string
|
|
97
|
+
The regex for matching against the id
|
|
98
|
+
timeseries_category_like: string
|
|
99
|
+
The regex for matching against the category id
|
|
100
|
+
timeseries_group_like: string
|
|
101
|
+
The regex for matching against the timeseries group id. This will default to pull only public datasets
|
|
102
|
+
bounding_office_like: string
|
|
103
|
+
The regex for matching against the location bounding office
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
cwms data type
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
# CHECKS
|
|
111
|
+
if office_id is None:
|
|
112
|
+
raise ValueError("Retrieve timeseries catalog requires an office")
|
|
113
|
+
|
|
114
|
+
dataset = "TIMESERIES"
|
|
115
|
+
endpoint = f"catalog/{dataset}"
|
|
116
|
+
params = {
|
|
117
|
+
"page": page,
|
|
118
|
+
"page-size": page_size,
|
|
119
|
+
"unit-system": unit_system,
|
|
120
|
+
"office": office_id,
|
|
121
|
+
"like": like,
|
|
122
|
+
"timeseries-category-like": timeseries_category_like,
|
|
123
|
+
"timeseries-group-like": timeseries_group_like,
|
|
124
|
+
"bounding-office-like": bounding_office_like,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
response = api.get(endpoint=endpoint, params=params, api_version=2)
|
|
128
|
+
return Data(response, selector="entries")
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Copyright (c) 2024
|
|
2
|
+
# United States Army Corps of Engineers - Hydrologic Engineering Center (USACE/HEC)
|
|
3
|
+
# All Rights Reserved. USACE PROPRIETARY/CONFIDENTIAL.
|
|
4
|
+
# Source may not be released without written approval from HEC
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import cwms.api as api
|
|
9
|
+
from cwms.types import JSON, Data
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_forecast_instances(
|
|
13
|
+
spec_id: Optional[str] = None,
|
|
14
|
+
office: Optional[str] = None,
|
|
15
|
+
designator: Optional[str] = None,
|
|
16
|
+
) -> Data:
|
|
17
|
+
"""
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
spec_id : str, optional
|
|
21
|
+
The forecast spec id.
|
|
22
|
+
office : str, optional
|
|
23
|
+
The spec office id.
|
|
24
|
+
designator : str, optional
|
|
25
|
+
The spec designator.
|
|
26
|
+
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
response : dict
|
|
30
|
+
the JSON response from CWMS Data API.
|
|
31
|
+
|
|
32
|
+
Raises
|
|
33
|
+
------
|
|
34
|
+
ValueError
|
|
35
|
+
If any of spec_id, office, or designator is None.
|
|
36
|
+
ClientError
|
|
37
|
+
If a 400 range error code response is returned from the server.
|
|
38
|
+
NoDataFoundError
|
|
39
|
+
If a 404 range error code response is returned from the server.
|
|
40
|
+
ServerError
|
|
41
|
+
If a 500 range error code response is returned from the server.
|
|
42
|
+
"""
|
|
43
|
+
if spec_id is None:
|
|
44
|
+
raise ValueError("Retrieving forecast instances requires an id")
|
|
45
|
+
if office is None:
|
|
46
|
+
raise ValueError("Retrieving forecast instances requires an office")
|
|
47
|
+
if designator is None:
|
|
48
|
+
raise ValueError("Retrieving forecast instances requires a designator")
|
|
49
|
+
endpoint = "forecast-instance"
|
|
50
|
+
|
|
51
|
+
params = {
|
|
52
|
+
"office": office,
|
|
53
|
+
"name": spec_id,
|
|
54
|
+
"designator": designator,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
response = api.get(endpoint, params)
|
|
58
|
+
return Data(response)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_forecast_instance(
|
|
62
|
+
spec_id: str,
|
|
63
|
+
office: str,
|
|
64
|
+
designator: str,
|
|
65
|
+
forecast_date: datetime,
|
|
66
|
+
issue_date: datetime,
|
|
67
|
+
) -> Data:
|
|
68
|
+
"""
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
spec_id : str
|
|
72
|
+
The ID of the forecast spec.
|
|
73
|
+
office : str
|
|
74
|
+
The ID of the office.
|
|
75
|
+
designator : str
|
|
76
|
+
The designator of the forecast spec
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
response : dict
|
|
81
|
+
the JSON response from CWMS Data API.
|
|
82
|
+
|
|
83
|
+
Raises
|
|
84
|
+
------
|
|
85
|
+
ValueError
|
|
86
|
+
If any of spec_id, office, or designator is None.
|
|
87
|
+
ClientError
|
|
88
|
+
If a 400 range error code response is returned from the server.
|
|
89
|
+
NoDataFoundError
|
|
90
|
+
If a 404 range error code response is returned from the server.
|
|
91
|
+
ServerError
|
|
92
|
+
If a 500 range error code response is returned from the server.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
if spec_id is None:
|
|
96
|
+
raise ValueError("Retrieve forecast instance requires an id")
|
|
97
|
+
if office is None:
|
|
98
|
+
raise ValueError("Retrieve a forecast instance requires an office")
|
|
99
|
+
if designator is None:
|
|
100
|
+
raise ValueError("Retrieve a forecast instance requires a designator")
|
|
101
|
+
if forecast_date is None:
|
|
102
|
+
raise ValueError("Retrieve a forecast instance requires a forecast date")
|
|
103
|
+
if issue_date is None:
|
|
104
|
+
raise ValueError("Retrieve a forecast instance requires a issue date")
|
|
105
|
+
|
|
106
|
+
endpoint = f"forecast-instance/{spec_id}"
|
|
107
|
+
|
|
108
|
+
params = {
|
|
109
|
+
"office": office,
|
|
110
|
+
"designator": designator,
|
|
111
|
+
"forecast-date": forecast_date.isoformat(),
|
|
112
|
+
"issue-date": issue_date.isoformat(),
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
response = api.get(endpoint, params)
|
|
116
|
+
return Data(response)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def store_forecast_instance(data: JSON) -> None:
|
|
120
|
+
"""
|
|
121
|
+
This method is used to store a forecast instance through CWMS Data API.
|
|
122
|
+
|
|
123
|
+
Parameters
|
|
124
|
+
----------
|
|
125
|
+
data : dict
|
|
126
|
+
A dictionary representing the JSON data to be stored.
|
|
127
|
+
If the `data` value is None, a `ValueError` will be raised.
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
None
|
|
132
|
+
|
|
133
|
+
Raises
|
|
134
|
+
------
|
|
135
|
+
ValueError
|
|
136
|
+
If dict is None.
|
|
137
|
+
ClientError
|
|
138
|
+
If a 400 range error code response is returned from the server.
|
|
139
|
+
NoDataFoundError
|
|
140
|
+
If a 404 range error code response is returned from the server.
|
|
141
|
+
ServerError
|
|
142
|
+
If a 500 range error code response is returned from the server.
|
|
143
|
+
"""
|
|
144
|
+
if data is None:
|
|
145
|
+
raise ValueError("Storing a forecast instance requires a JSON data dictionary")
|
|
146
|
+
endpoint = "forecast-instance"
|
|
147
|
+
|
|
148
|
+
return api.post(endpoint, data, params=None)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def delete_forecast_instance(
|
|
152
|
+
spec_id: str,
|
|
153
|
+
office: str,
|
|
154
|
+
designator: str,
|
|
155
|
+
forecast_date: datetime,
|
|
156
|
+
issue_date: datetime,
|
|
157
|
+
) -> None:
|
|
158
|
+
"""
|
|
159
|
+
Parameters
|
|
160
|
+
----------
|
|
161
|
+
spec_id : str
|
|
162
|
+
The ID of the forecast spec.
|
|
163
|
+
office : str
|
|
164
|
+
The ID of the office.
|
|
165
|
+
designator : str
|
|
166
|
+
The designator of the forecast spec
|
|
167
|
+
forecast_date : datetime
|
|
168
|
+
The forecast date of the forecast instance
|
|
169
|
+
issue_date : datetime
|
|
170
|
+
The forecast issue date of the forecast instance
|
|
171
|
+
|
|
172
|
+
Returns
|
|
173
|
+
-------
|
|
174
|
+
response : dict
|
|
175
|
+
the JSON response from CWMS Data API.
|
|
176
|
+
|
|
177
|
+
Raises
|
|
178
|
+
------
|
|
179
|
+
ValueError
|
|
180
|
+
If any of spec_id, office, designator,
|
|
181
|
+
forecast_date, or issue_date is None.
|
|
182
|
+
ClientError
|
|
183
|
+
If a 400 range error code response is returned from the server.
|
|
184
|
+
NoDataFoundError
|
|
185
|
+
If a 404 range error code response is returned from the server.
|
|
186
|
+
ServerError
|
|
187
|
+
If a 500 range error code response is returned from the server.
|
|
188
|
+
"""
|
|
189
|
+
if spec_id is None:
|
|
190
|
+
raise ValueError("Deleting a forecast instance requires an id")
|
|
191
|
+
if office is None:
|
|
192
|
+
raise ValueError("Deleting a forecast instance requires an office")
|
|
193
|
+
if designator is None:
|
|
194
|
+
raise ValueError("Deleting a forecast instance requires a designator")
|
|
195
|
+
if forecast_date is None:
|
|
196
|
+
raise ValueError("Deleting a forecast instance requires a forecast date")
|
|
197
|
+
if issue_date is None:
|
|
198
|
+
raise ValueError("Deleting a forecast instance requires a issue date")
|
|
199
|
+
|
|
200
|
+
endpoint = f"forecast-instance/{spec_id}"
|
|
201
|
+
|
|
202
|
+
params = {
|
|
203
|
+
"office": office,
|
|
204
|
+
"designator": designator,
|
|
205
|
+
"forecast-date": forecast_date.isoformat(),
|
|
206
|
+
"issue-date": issue_date.isoformat(),
|
|
207
|
+
}
|
|
208
|
+
return api.delete(endpoint, params)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Copyright (c) 2024
|
|
2
|
+
# United States Army Corps of Engineers - Hydrologic Engineering Center (USACE/HEC)
|
|
3
|
+
# All Rights Reserved. USACE PROPRIETARY/CONFIDENTIAL.
|
|
4
|
+
# Source may not be released without written approval from HEC
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import cwms.api as api
|
|
8
|
+
from cwms.types import JSON, Data, DeleteMethod
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_forecast_specs(
|
|
12
|
+
id_mask: Optional[str] = None,
|
|
13
|
+
office: Optional[str] = None,
|
|
14
|
+
designator_mask: Optional[str] = None,
|
|
15
|
+
source_entity: Optional[str] = None,
|
|
16
|
+
) -> Data:
|
|
17
|
+
"""
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
id_mask : str, optional
|
|
21
|
+
The regex filter for the forecast spec id.
|
|
22
|
+
office : str, optional
|
|
23
|
+
The regex filter for the forecast spec id.
|
|
24
|
+
designator_mask : str, optional
|
|
25
|
+
The regex filter for the forecast spec id.
|
|
26
|
+
source_entity : str, optional
|
|
27
|
+
The regex filter for the forecast spec id.
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
response : dict
|
|
32
|
+
the JSON response from CWMS Data API.
|
|
33
|
+
|
|
34
|
+
Raises
|
|
35
|
+
------
|
|
36
|
+
ClientError
|
|
37
|
+
If a 400 range error code response is returned from the server.
|
|
38
|
+
NoDataFoundError
|
|
39
|
+
If a 404 range error code response is returned from the server.
|
|
40
|
+
ServerError
|
|
41
|
+
If a 500 range error code response is returned from the server.
|
|
42
|
+
"""
|
|
43
|
+
endpoint = "forecast-spec"
|
|
44
|
+
|
|
45
|
+
params = {
|
|
46
|
+
"office": office,
|
|
47
|
+
"id_mask": id_mask,
|
|
48
|
+
"designator-mask": designator_mask,
|
|
49
|
+
"source-entity": source_entity,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
response = api.get(endpoint, params)
|
|
53
|
+
return Data(response)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_forecast_spec(spec_id: str, office: str, designator: str) -> Data:
|
|
57
|
+
"""
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
spec_id : str
|
|
61
|
+
The ID of the forecast spec.
|
|
62
|
+
office : str
|
|
63
|
+
The ID of the office.
|
|
64
|
+
designator : str
|
|
65
|
+
The designator of the forecast spec
|
|
66
|
+
|
|
67
|
+
Returns
|
|
68
|
+
-------
|
|
69
|
+
response : dict
|
|
70
|
+
the JSON response from CWMS Data API.
|
|
71
|
+
|
|
72
|
+
Raises
|
|
73
|
+
------
|
|
74
|
+
ValueError
|
|
75
|
+
If any of spec_id, office, or designator is None.
|
|
76
|
+
ClientError
|
|
77
|
+
If a 400 range error code response is returned from the server.
|
|
78
|
+
NoDataFoundError
|
|
79
|
+
If a 404 range error code response is returned from the server.
|
|
80
|
+
ServerError
|
|
81
|
+
If a 500 range error code response is returned from the server.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
if spec_id is None:
|
|
85
|
+
raise ValueError("Retrieve forecast spec requires an id")
|
|
86
|
+
if office is None:
|
|
87
|
+
raise ValueError("Retrieve a forecast spec requires an office")
|
|
88
|
+
if designator is None:
|
|
89
|
+
raise ValueError("Retrieve a forecast spec requires a designator")
|
|
90
|
+
|
|
91
|
+
endpoint = f"forecast-spec/{spec_id}"
|
|
92
|
+
|
|
93
|
+
params = {
|
|
94
|
+
"office": office,
|
|
95
|
+
"designator": designator,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
response = api.get(endpoint, params)
|
|
99
|
+
return Data(response)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def store_forecast_spec(data: JSON) -> None:
|
|
103
|
+
"""
|
|
104
|
+
This method is used to store a forecast spec through CWMS Data API.
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
data : dict
|
|
109
|
+
A dictionary representing the JSON data to be stored.
|
|
110
|
+
If the `data` value is None, a `ValueError` will be raised.
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
None
|
|
115
|
+
|
|
116
|
+
Raises
|
|
117
|
+
------
|
|
118
|
+
ValueError
|
|
119
|
+
If dict is None.
|
|
120
|
+
ClientError
|
|
121
|
+
If a 400 range error code response is returned from the server.
|
|
122
|
+
NoDataFoundError
|
|
123
|
+
If a 404 range error code response is returned from the server.
|
|
124
|
+
ServerError
|
|
125
|
+
If a 500 range error code response is returned from the server.
|
|
126
|
+
"""
|
|
127
|
+
if data is None:
|
|
128
|
+
raise ValueError("Storing a forecast spec requires a JSON data dictionary")
|
|
129
|
+
endpoint = "forecast-spec"
|
|
130
|
+
|
|
131
|
+
return api.post(endpoint, data)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def delete_forecast_spec(
|
|
135
|
+
spec_id: str,
|
|
136
|
+
office: str,
|
|
137
|
+
designator: str,
|
|
138
|
+
delete_method: DeleteMethod,
|
|
139
|
+
) -> None:
|
|
140
|
+
"""
|
|
141
|
+
Parameters
|
|
142
|
+
----------
|
|
143
|
+
spec_id : str
|
|
144
|
+
The ID of the forecast spec.
|
|
145
|
+
office : str
|
|
146
|
+
The ID of the office.
|
|
147
|
+
designator : str
|
|
148
|
+
The designator of the forecast spec
|
|
149
|
+
delete_method: DeleteMethod
|
|
150
|
+
The method to use to delete forecast spec data
|
|
151
|
+
|
|
152
|
+
Returns
|
|
153
|
+
-------
|
|
154
|
+
response : dict
|
|
155
|
+
the JSON response from CWMS Data API.
|
|
156
|
+
|
|
157
|
+
Raises
|
|
158
|
+
------
|
|
159
|
+
ValueError
|
|
160
|
+
If any of spec_id, office, or designator is None.
|
|
161
|
+
ClientError
|
|
162
|
+
If a 400 range error code response is returned from the server.
|
|
163
|
+
NoDataFoundError
|
|
164
|
+
If a 404 range error code response is returned from the server.
|
|
165
|
+
ServerError
|
|
166
|
+
If a 500 range error code response is returned from the server.
|
|
167
|
+
"""
|
|
168
|
+
if spec_id is None:
|
|
169
|
+
raise ValueError("Deleting a forecast spec requires an id")
|
|
170
|
+
if office is None:
|
|
171
|
+
raise ValueError("Deleting a forecast spec requires an office")
|
|
172
|
+
if designator is None:
|
|
173
|
+
raise ValueError("Deleting a forecast spec requires a designator")
|
|
174
|
+
|
|
175
|
+
endpoint = f"forecast-spec/{spec_id}"
|
|
176
|
+
params = {
|
|
177
|
+
"office": office,
|
|
178
|
+
"designator": designator,
|
|
179
|
+
"method": delete_method.name,
|
|
180
|
+
}
|
|
181
|
+
return api.delete(endpoint, params)
|