Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
c322402
fix(source): enable tag paramter for json requests to be Null
jacob-a-brown Apr 27, 2026
ec9f6e3
fix(config): update config/false agencies for bicarbonate
jacob-a-brown Apr 27, 2026
dcaf8d3
fix(USGS): use new USGS API endpoints for water level retrieval
jacob-a-brown Apr 27, 2026
036e9e7
fix(usgs): update transformer for combined-metadata endpoint
jacob-a-brown Apr 28, 2026
2425094
fix(usgs): update USGS water level retrieval for new APIs
jacob-a-brown Apr 28, 2026
500a0b1
fix(usgs): set limit to 50000 to avoid pagination/rate limits
jacob-a-brown Apr 29, 2026
d93869d
refactor(usgs): check usgs health with get request
jacob-a-brown Apr 29, 2026
12247f5
fix(config): set agencies for bicarbonate
jacob-a-brown Apr 29, 2026
b342922
fix(usgs): add timeout to usgs api calls and retry logic if errors
jacob-a-brown Apr 29, 2026
3f2d104
fix(usgs): add retry logic to pagination retrieval
jacob-a-brown Apr 29, 2026
6005f70
refactor(usgs): set datum in site transformer
jacob-a-brown Apr 29, 2026
dba8213
fix(usgs): return False if health check fails
jacob-a-brown Apr 29, 2026
b17bbde
fix(usgs): shorten error message
jacob-a-brown Apr 29, 2026
5570c36
fix(usgs): set limit of 1 for health check
jacob-a-brown Apr 29, 2026
836243e
fix(usgs): remove unused imports
jacob-a-brown Apr 29, 2026
a51b13e
fix(usgs): call self.warn not warning
jacob-a-brown Apr 29, 2026
31271c6
fix(usgs): remove hardcoded API key
jacob-a-brown Apr 30, 2026
deed95f
fix(usgs): have users pass USGS API keys
jacob-a-brown Apr 30, 2026
539fb13
fix(usgs): update README documentation
jacob-a-brown Apr 30, 2026
6897f28
fix(documentation): make README more readable
jacob-a-brown Apr 30, 2026
1d2bac9
fix(documentation): link to USGS API key acquisition
jacob-a-brown Apr 30, 2026
5f886bb
fix(usgs): exit if rate limit exceeded
jacob-a-brown Apr 30, 2026
f9908fd
fix(usgs): disallow partial records if a rate limit error is hit
jacob-a-brown Apr 30, 2026
7082957
fix(usgs): implement retries with exponential backoff
jacob-a-brown Apr 30, 2026
148208b
refactor(exceptions): put custom exceptions in own script
jacob-a-brown Apr 30, 2026
f760bbb
fix(usgs): catch JSONDecodeError and retry
jacob-a-brown Apr 30, 2026
1aff110
fix(usgs): check status_code for health check
jacob-a-brown Apr 30, 2026
0af670e
fix(usgs): revert persister.records on partial completion
jacob-a-brown Apr 30, 2026
690ba41
fix(partial records): don't persist partial records
jacob-a-brown Apr 30, 2026
7424177
fix(usgs): make site source USGS-NWIS to correspond with field measur…
jacob-a-brown Apr 30, 2026
013406d
feature(usgs): add dt params
jacob-a-brown Apr 30, 2026
7d38c37
note(usgs): type hint variables
jacob-a-brown Apr 30, 2026
8183300
test(usgs): use .env for api key for tests
jacob-a-brown Apr 30, 2026
9bf3fc2
fix(usgs): Handle USGS rate limit and partial/no data errors
jacob-a-brown Apr 30, 2026
43409f3
fix(test): don't override parent setup fixture
jacob-a-brown Apr 30, 2026
441ff81
fix(test): remove env var outside of fixture
jacob-a-brown Apr 30, 2026
e524ec7
fix(requirements): download python-dotenv
jacob-a-brown Apr 30, 2026
7af57d6
fix(test): load usgs api key from .env for every test
jacob-a-brown Apr 30, 2026
d7382bd
fix(unifier): remove partial persister.records on failure
jacob-a-brown Apr 30, 2026
245b01f
fix(usgs): use truthiness check for usgs api key header
jacob-a-brown Apr 30, 2026
f5b2e2f
fix(style): two blank lines between classes
jacob-a-brown Apr 30, 2026
85ab811
fix(usgs): fail health check on non 2xx status codes
jacob-a-brown Apr 30, 2026
5cf9904
test(usgs api key): test usgs api key is env var when set via --usgs-…
jacob-a-brown Apr 30, 2026
4f3b913
fix(test): reset usgs api key after each test
jacob-a-brown May 1, 2026
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
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ Data comes from the following sources. We are continuously adding new sources as
- Available data: `water levels`
- [Pecos Valley Artesian Conservancy District (PVACD)](https://st2.newmexicowaterdata.org/FROST-Server/v1.1/Locations?$filter=properties/agency%20eq%20%27PVACD%27)
- Available data: `water levels`
- [USGS (NWIS)](https://waterdata.usgs.gov/nwis)
- [USGS (NWIS)](https://api.waterdata.usgs.gov/docs/)
- Available data: `water levels`
- **IMPORTANT:** The USGS now uses API keys. To prevent yourself from hitting the rate limit please [acquire an API key](https://api.waterdata.usgs.gov/signup/), save it, and provide it via the `--usgs-api-key` flag when gathering water level data from the USGS.
- [Water Quality Portal (WQP)](https://www.waterqualitydata.us/)
- Available data: `water levels`, `water quality`

Expand Down Expand Up @@ -189,6 +190,14 @@ The Data Integration Engine enables the user to obtain groundwater level and gro
- `--no-pvacd` to exclude Pecos Valley Artesian Convservancy District (PVACD) data
- `--no-wqp` to exclude Water Quality Portal (WQP) data

### USGS API Keys

The USGS now uses [API keys](https://api.waterdata.usgs.gov/signup/) to increase the query rate limit to their APIs. If you intend to include USGS water level data in your output please acquire an API key, save it somewhere, and provide it via the `--usgs-api-key` flag. For example:

```
die weave waterlevels --output-type timeseries_unified --usgs-api-key FAKE_API_KEY
```

### Geographic Filters [In Development]

The following flags can be used to geographically filter data:
Expand Down
13 changes: 12 additions & 1 deletion backend/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,19 @@ def get_config_and_false_agencies(self):
"nwis",
"pvacd",
]
elif self.parameter in [BICARBONATE]:
config_agencies = ["nmbgmr_amp", "nmed_dwb", "nmose_isc_seven_rivers", "wqp"]
false_agencies = [
"bor",
"bernco",
"cabq",
"ebid",
"nmose_roswell",
"nmose_pod",
"nwis",
"pvacd",
]
elif self.parameter in [
BICARBONATE,
CALCIUM,
CHLORIDE,
FLUORIDE,
Expand Down
8 changes: 5 additions & 3 deletions backend/connectors/bor/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def get_records(self):
# locationTypeId 10 is for wells
url = "https://data.usbr.gov/rise/api/location"
params = {"stateId": "NM", "locationTypeId": 10}
return self._execute_json_request(url, params)
return self._execute_json_request(url, params, tag="data")


def parse_dt(dt):
Expand Down Expand Up @@ -119,13 +119,14 @@ def get_records(self, site_record):
code = get_analyte_search_param(self.config.parameter, BOR_ANALYTE_MAPPING)

catalog_record_data = self._execute_json_request(
f"https://data.usbr.gov{site_record.catalogRecords[0]['id']}"
f"https://data.usbr.gov{site_record.catalogRecords[0]['id']}",
tag="data"
)
catalog_items = catalog_record_data["relationships"]["catalogItems"]["data"]

for i, item in enumerate(self._reorder_catalog_items(catalog_items)):

data = self._execute_json_request(f'https://data.usbr.gov{item["id"]}')
data = self._execute_json_request(f'https://data.usbr.gov{item["id"]}', tag="data")
if not data:
continue

Expand All @@ -142,6 +143,7 @@ def get_records(self, site_record):
return self._execute_json_request(
"https://data.usbr.gov/rise/api/result",
params={"itemId": data["attributes"]["_id"]},
tag="data"
)


Expand Down
6 changes: 4 additions & 2 deletions backend/connectors/isc_seven_rivers/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def health(self):
def get_records(self):
return self._execute_json_request(
_make_url("getMonitoringPoints.ashx"),
tag="data",
)


Expand All @@ -100,7 +101,7 @@ def _get_analyte_id_and_name(self, analyte):
""" """
if self._analyte_ids is None:

resp = self._execute_json_request(_make_url("getAnalytes.ashx"))
resp = self._execute_json_request(_make_url("getAnalytes.ashx"), tag="data")
if resp:
self._analyte_ids = {r["name"]: r["id"] for r in resp}

Expand Down Expand Up @@ -164,7 +165,7 @@ def get_records(self, site_record):
self._source_parameter_name = analyte_id_and_name["name"]

return self._execute_json_request(
_make_url("getReadings.ashx"), params=params
_make_url("getReadings.ashx"), params=params, tag="data"
)


Expand All @@ -184,6 +185,7 @@ def get_records(self, site_record):
return self._execute_json_request(
_make_url("getWaterLevels.ashx"),
params=params,
tag="data",
)

def _clean_records(self, records):
Expand Down
Loading