Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ repos:
language: pygrep
entry: \b([A-Za-z]+)[ \t]+\1\b
types: [text]
exclude: ^tests/data/
exclude: ^(tests/data/|dataretrieval/codes/_county_table\.py$)

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.16.5
Expand Down
30 changes: 30 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,36 @@ a service names a thing `site-types` or `site_type_code`, that is its vocabulary
and is reproduced rather than translated. Prose that is not about one particular
adapter says *monitoring location*.

**State** — A US state, the District of Columbia, or one of the five
territories, as the place a monitoring location is in.

*Domain term.* The services spell it differently: Water Data `state_name`
(`"Wisconsin"`) and `state_code` (`"55"`), the Water Data statistics service
`state_code` (`"US:55"`), NGWMN `state_name` on `sites` but a postal `state`
(`"WI"`) on `providers`, NWDC `stateCd:WI`, WQP `statecode`, NWIS `stateCd`.
Each adapter keeps those parameters. Where an adapter also takes `state`, that is
a *unified argument* and not a service's spelling.

**County** — A county or county equivalent (parish, borough, census area,
independent city, municipio, Connecticut planning region) within a state.

*Domain term.* A county's three-digit code repeats across states, so it is
identified by state and county together, and the services spell the pair
differently: Water Data `state_code` + `county_code` (`"55"` + `"025"`) or
`county_name` (`"Dane County"`), the statistics service one `county_code`
(`"US:55:025"`), NGWMN `state_name` + `county_name`, NWDC `countyCd:55025`, WQP
`countycode`, NWIS `countyCd`. Where an adapter also takes `county`, that is a
unified argument.

**Unified argument** — A getter argument named by the package for a domain
concept, accepting every common spelling of it and sending each service the one
it filters on: `state` (a name, postal code, or FIPS code) and `county` (a
FIPS code, or a name with its state). It is added beside the service's own
parameters, never in their place; the rule is ADR 0013's.

*Core term.* The argument names are the package's; the conversion tables are in
`dataretrieval.codes`.

**Metadata** — The second half of every getter's return: the request URL, the
elapsed time, and the response headers. Describes the *retrieval*, not the data.

Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
**10/01/2026:** **New:** a `county` argument, like `state`, on `waterdata.get_monitoring_locations()`, `waterdata.get_combined_metadata()`, `waterdata.get_stats_por()`, `waterdata.get_stats_date_range()`, `ngwmn.get_sites()`, and `nwdc.get_wateruse()`. It accepts a five-digit FIPS code (`"55025"` or `55025`), the statistics service's `"US:55:025"`, or a name or three-digit code with its state (`county="Dane", state="WI"`), and sends each service the fields it filters on: Water Data `state_code` + `county_code`, statistics `county_code="US:55:025"`, NGWMN `state_name` + `county_name`, NWDC `countyCd:55025`. With `county`, `state` names the counties' state rather than filtering separately. `dataretrieval.codes.to_county()` exposes the conversion; its table is the Water Data `counties` collection for the 50 states, DC, and the territories, and a live test compares the two. The native `county_code` / `county_name` parameters are unchanged, and passing one with `county` raises `ValueError`. Counties in several states are sent to the Water Data location collections as a CQL `filter` OR-ing (state, county) pairs, so they cannot be combined with your own `filter`; `ngwmn.get_sites()` takes counties from one state per call, and raises for Connecticut, whose NGWMN sites are still filed under the counties the state replaced with planning regions in 2022 (pass `county_name="Hartford County"` instead). WQP and legacy NWIS are unchanged, as they are for `state`. **Bug fix:** `ngwmn.get_sites(county_name=[...])` with more than one name returned no rows: NGWMN matches nothing for a comma-joined `county_name`. Multi-value `sites` filters are now POSTed as CQL2, as the Water Data location collections' are. **Behavior change:** `nwdc.get_wateruse(state=..., county=...)` no longer raises for naming two locations; the state qualifies the county, and a county outside it raises. An unknown county such as Connecticut's retired `"09003"` now raises locally instead of HTTP 400.

**10/01/2026:** **Behavior change:** the numeric columns of the `waterdata` OGC getters and `ngwmn` — `value`, `altitude`, `drainage_area`, and the other measurement columns — are always `float64`. They were `int64` whenever every value in a response happened to be whole, so the same column changed dtype between calls, breaking concatenation, schema checks, and typed stores (#428). **Behavior change:** a value in a numeric or datetime column that cannot be parsed still becomes `NaN`/`NaT`, but now emits a `UserWarning` naming the column and the count, so a parse failure is distinguishable from a missing value. Repeat the call with `convert_type=False` to see the raw values. Expect it from `waterdata.get_monitoring_locations()`: its `construction_date` holds partial dates (`1925`, `194805`, `19100000`) that a datetime column cannot represent, and they were already being dropped without notice — 7,306 of 24,089 values in a query for Maryland groundwater sites.

**10/01/2026:** **Bug fix:** `waterdata.get_nearest_continuous` turned a missing target (`NaT`, `None`, `NaN`, or `""`) into a `(time >= 'nan' AND time <= 'nan')` clause and sent it to the service, including when only one entry of a list was missing. It now raises `ValueError` before any request, naming how many targets are missing and the position of the first. Targets pandas cannot parse now raise a `ValueError` that names `targets`, rather than pandas' message, which named no argument and suggested a `format=` the getter does not accept. **Bug fix:** the same getter's `window` was not validated: a missing window (`None`, `'NaT'`) failed with an `AttributeError` while the filter was built, and a negative one (`'-PT5M'`) inverted every bound, so the query matched nothing and returned an empty frame indistinguishable from a gap in the data. Both now raise `ValueError` naming `window`; a zero window remains an exact-match query. **Behavior change:** a bare-number `window` now raises `TypeError`; pandas read `window=450` as 450 nanoseconds, so it matched almost nothing. Pass a duration such as `window='PT450S'`. Numeric `targets` raise `TypeError` for the same reason: `targets=[1.5e9]` meant as epoch seconds was read as 1970-01-01T00:00:01.5Z. **Behavior change:** calls that previously sent a malformed filter now fail locally, so a caller passing targets with gaps must drop or fill them first.
Expand Down
8 changes: 5 additions & 3 deletions dataretrieval/codes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""Facade over the ``states`` and ``timezones`` lookup tables.
"""Facade over the ``states``, ``counties``, and ``timezones`` lookup tables.

Re-exports the state code maps, their normalizers (``to_state``,
``apply_state``), and the ``tz`` UTC-offset map.
Re-exports the state code maps and their normalizers (``to_state``,
``apply_state``), the county map and its normalizers (``to_county``,
``apply_county``), and the ``tz`` UTC-offset map.
"""

from .counties import *
from .states import *
from .timezones import *
Loading
Loading