cwms-python 1.0.0__tar.gz → 1.0.1__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-1.0.0 → cwms_python-1.0.1}/PKG-INFO +1 -1
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/api.py +32 -11
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/timeseries/timeseries.py +2 -2
- {cwms_python-1.0.0 → cwms_python-1.0.1}/pyproject.toml +1 -1
- {cwms_python-1.0.0 → cwms_python-1.0.1}/LICENSE +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/README.md +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/__init__.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/catalog/blobs.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/catalog/catalog.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/catalog/clobs.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/cwms_types.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/forecast/forecast_instance.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/forecast/forecast_spec.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/levels/location_levels.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/levels/specified_levels.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/locations/gate_changes.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/locations/location_groups.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/locations/physical_locations.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/measurements/measurements.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/outlets/outlets.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/outlets/virtual_outlets.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/projects/project_lock_rights.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/projects/project_locks.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/projects/projects.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/projects/water_supply/accounting.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/ratings/ratings.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/ratings/ratings_spec.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/ratings/ratings_template.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/standard_text/standard_text.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/timeseries/timeseries_bin.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/timeseries/timeseries_group.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/timeseries/timeseries_identifier.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/timeseries/timeseries_profile.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/timeseries/timeseries_profile_instance.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/timeseries/timeseries_profile_parser.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/timeseries/timeseries_txt.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/turbines/turbines.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/utils/__init__.py +0 -0
- {cwms_python-1.0.0 → cwms_python-1.0.1}/cwms/utils/checks.py +0 -0
|
@@ -29,6 +29,7 @@ the error.
|
|
|
29
29
|
import base64
|
|
30
30
|
import json
|
|
31
31
|
import logging
|
|
32
|
+
from http import HTTPStatus
|
|
32
33
|
from json import JSONDecodeError
|
|
33
34
|
from typing import Any, Optional, cast
|
|
34
35
|
|
|
@@ -72,9 +73,9 @@ class InvalidVersion(Exception):
|
|
|
72
73
|
class ApiError(Exception):
|
|
73
74
|
"""CWMS Data Api Error.
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
Light wrapper around a response-like object (e.g., requests.Response or a
|
|
77
|
+
test stub with url, status_code, reason, and content attributes). Produces
|
|
78
|
+
a concise, single-line error message with an optional hint.
|
|
78
79
|
"""
|
|
79
80
|
|
|
80
81
|
def __init__(self, response: Response):
|
|
@@ -91,23 +92,37 @@ class ApiError(Exception):
|
|
|
91
92
|
message += "."
|
|
92
93
|
|
|
93
94
|
# Add additional context to help the user resolve the issue.
|
|
94
|
-
|
|
95
|
+
hint = self.hint()
|
|
96
|
+
if hint:
|
|
95
97
|
message += f" {hint}"
|
|
96
98
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
# Optional content (decoded if bytes)
|
|
100
|
+
content = getattr(self.response, "content", None)
|
|
101
|
+
if content:
|
|
102
|
+
if isinstance(content, bytes):
|
|
103
|
+
try:
|
|
104
|
+
text = content.decode("utf-8", errors="replace")
|
|
105
|
+
except Exception:
|
|
106
|
+
text = repr(content)
|
|
107
|
+
else:
|
|
108
|
+
text = str(content)
|
|
109
|
+
message += f" {text}"
|
|
99
110
|
|
|
100
111
|
return message
|
|
101
112
|
|
|
102
113
|
def hint(self) -> str:
|
|
103
|
-
"""Return a
|
|
114
|
+
"""Return a short hint based on HTTP status code."""
|
|
115
|
+
status = getattr(self.response, "status_code", None)
|
|
104
116
|
|
|
105
|
-
if
|
|
117
|
+
if status == 429:
|
|
118
|
+
return "Too many requests made."
|
|
119
|
+
if status == 400:
|
|
106
120
|
return "Check that your parameters are correct."
|
|
107
|
-
|
|
121
|
+
if status == 404:
|
|
108
122
|
return "May be the result of an empty query."
|
|
109
|
-
|
|
110
|
-
|
|
123
|
+
|
|
124
|
+
# No hint for other codes
|
|
125
|
+
return ""
|
|
111
126
|
|
|
112
127
|
|
|
113
128
|
def init_session(
|
|
@@ -227,6 +242,12 @@ def _process_response(response: Response) -> Any:
|
|
|
227
242
|
return response.text
|
|
228
243
|
if content_type.startswith("image/"):
|
|
229
244
|
return base64.b64encode(response.content).decode("utf-8")
|
|
245
|
+
# Handle excel content types
|
|
246
|
+
if content_type in [
|
|
247
|
+
"application/vnd.ms-excel",
|
|
248
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
249
|
+
]:
|
|
250
|
+
return response.content
|
|
230
251
|
# Fallback for remaining content types
|
|
231
252
|
return response.content.decode("utf-8")
|
|
232
253
|
except JSONDecodeError as error:
|
|
@@ -606,8 +606,8 @@ def store_timeseries(
|
|
|
606
606
|
for chunk in chunks:
|
|
607
607
|
future = executor.submit(
|
|
608
608
|
api.post, # The function to execute
|
|
609
|
-
endpoint,
|
|
610
|
-
data
|
|
609
|
+
endpoint,
|
|
610
|
+
chunk, # The chunk of data to store
|
|
611
611
|
params,
|
|
612
612
|
)
|
|
613
613
|
futures.append(future) # Add the future to the list
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|