Skip to content

Commit 90cb019

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix/format-api-dates-iso8601
# Conflicts: # tests/waterdata_utils_test.py
2 parents 9bde9e1 + 98b3057 commit 90cb019

5 files changed

Lines changed: 71 additions & 24 deletions

File tree

dataretrieval/nldi.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ def _validate_data_source(data_source: str):
486486
def _validate_navigation_mode(navigation_mode: str):
487487
navigation_mode = navigation_mode.upper()
488488
if navigation_mode not in ("UM", "DM", "UT", "DD"):
489-
raise TypeError(f"Invalid navigation mode '{navigation_mode}'")
489+
raise ValueError(f"Invalid navigation mode '{navigation_mode}'")
490490

491491

492492
def _validate_feature_source_comid(

dataretrieval/waterdata/utils.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -915,11 +915,13 @@ def _handle_stats_nesting(
915915
# otherwise return a geodataframe
916916
if not geopd:
917917
df = pd.json_normalize(body["features"]).drop(
918-
columns=["type", "properties.data"]
918+
columns=["type", "properties.data"], errors="ignore"
919919
)
920920
df.columns = df.columns.str.split(".").str[-1]
921921
else:
922-
df = gpd.GeoDataFrame.from_features(body["features"]).drop(columns=["data"])
922+
df = gpd.GeoDataFrame.from_features(body["features"]).drop(
923+
columns=["data"], errors="ignore"
924+
)
923925

924926
# Unnest json features, properties, data, and values while retaining necessary
925927
# metadata to merge with main dataframe.

dataretrieval/wqp.py

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -127,31 +127,23 @@ def get_results(
127127
kwargs = _check_kwargs(kwargs)
128128

129129
if legacy is True:
130-
if (
131-
"dataProfile" in kwargs
132-
and kwargs["dataProfile"] not in result_profiles_legacy
133-
):
134-
raise TypeError(
135-
f"dataProfile {kwargs['dataProfile']} is not a legacy profile.",
136-
f"Valid options are {result_profiles_legacy}.",
137-
)
138-
130+
valid_profiles = result_profiles_legacy
131+
kind = "legacy"
139132
url = wqp_url("Result")
140-
141133
else:
142-
if (
143-
"dataProfile" in kwargs
144-
and kwargs["dataProfile"] not in result_profiles_wqx3
145-
):
146-
raise TypeError(
147-
f"dataProfile {kwargs['dataProfile']} is not a valid WQX3.0"
148-
f"profile. Valid options are {result_profiles_wqx3}.",
149-
)
150-
else:
151-
kwargs["dataProfile"] = "fullPhysChem"
152-
134+
valid_profiles = result_profiles_wqx3
135+
kind = "WQX3.0"
153136
url = wqx3_url("Result")
154137

138+
profile = kwargs.get("dataProfile")
139+
if profile is not None and profile not in valid_profiles:
140+
raise ValueError(
141+
f"dataProfile {profile!r} is not a valid {kind} profile. "
142+
f"Valid options are {valid_profiles}."
143+
)
144+
if legacy is not True and profile is None:
145+
kwargs["dataProfile"] = "fullPhysChem"
146+
155147
response = query(url, kwargs, delimiter=";", ssl_check=ssl_check)
156148

157149
df = pd.read_csv(StringIO(response.text), delimiter=",", low_memory=False)

tests/waterdata_utils_test.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from dataretrieval.waterdata.utils import (
66
_format_api_dates,
77
_get_args,
8+
_handle_stats_nesting,
89
_walk_pages,
910
)
1011

@@ -83,6 +84,36 @@ def test_walk_pages_multiple_mocked():
8384
assert mock_client.request.call_args[0][1] == "https://example.com/page2"
8485

8586

87+
def test_handle_stats_nesting_tolerates_missing_drop_columns():
88+
"""If the upstream stats response shape ever changes such that one of
89+
the columns we try to drop ("type", "properties.data") is absent, the
90+
function should still return a DataFrame instead of raising KeyError.
91+
"""
92+
body = {
93+
"next": None,
94+
"features": [
95+
{
96+
"properties": {
97+
"monitoring_location_id": "USGS-12345",
98+
"data": [
99+
{
100+
"parameter_code": "00060",
101+
"unit_of_measure": "ft^3/s",
102+
"parent_time_series_id": "ts-1",
103+
"values": [{"statistic_id": "mean", "value": 10.0}],
104+
}
105+
],
106+
},
107+
}
108+
],
109+
}
110+
111+
df = _handle_stats_nesting(body, geopd=False)
112+
113+
assert len(df) == 1
114+
assert df["monitoring_location_id"].iloc[0] == "USGS-12345"
115+
116+
86117
# --- _format_api_dates -------------------------------------------------------
87118

88119

tests/wqp_test.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,25 @@ def test_check_kwargs():
216216
kwargs = {"mimeType": "foo"}
217217
with pytest.raises(ValueError):
218218
kwargs = _check_kwargs(kwargs)
219+
220+
221+
def test_get_results_wqx3_preserves_user_dataProfile(requests_mock):
222+
"""A valid user-supplied WQX3.0 profile must not be overwritten.
223+
224+
Regression: previously the `else` branch of the `dataProfile` validation
225+
triggered whenever the value was *not invalid*, including any valid
226+
user-supplied profile, silently overwriting it with 'fullPhysChem'.
227+
"""
228+
request_url = (
229+
"https://www.waterqualitydata.us/wqx3/Result/search?"
230+
"siteid=UTAHDWQ_WQX-4993795&mimeType=csv&dataProfile=narrow"
231+
)
232+
response_file_path = "tests/data/wqp3_results.txt"
233+
mock_request(requests_mock, request_url, response_file_path)
234+
235+
df, _md = get_results(
236+
legacy=False, siteid="UTAHDWQ_WQX-4993795", dataProfile="narrow"
237+
)
238+
assert isinstance(df, DataFrame)
239+
sent = requests_mock.request_history[-1]
240+
assert sent.qs.get("dataprofile") == ["narrow"]

0 commit comments

Comments
 (0)