Skip to content

Feature/asos reaper#7

Open
sray014 wants to merge 4 commits into
mainfrom
feature/asos-reaper
Open

Feature/asos reaper#7
sray014 wants to merge 4 commits into
mainfrom
feature/asos-reaper

Conversation

@sray014

@sray014 sray014 commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Adds ASOS reaper

@sray014 sray014 requested a review from cheginit June 22, 2026 17:27

@cheginit cheginit left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a few comments.

Comment on lines +101 to +116
def _build_url(self) -> str:
"""Build the IEM ASOS request URL with query parameters."""
params = [
("network", self.network),
("year1", self.start_date.year),
("month1", self.start_date.month),
("day1", self.start_date.day),
("year2", self.end_date.year),
("month2", self.end_date.month),
("day2", self.end_date.day),
("format", "comma"),
("latlon", "yes"),
]
for var in self.data_vars:
params.append(("data", var))
return f"{BASE_URL}?{urlencode(params)}"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rewrite with doseq:

def _build_url(self) -> str:
    params = {
        "network": self.network,
        "year1": self.start_date.year,
        "month1": self.start_date.month,
        "day1": self.start_date.day,
        "year2": self.end_date.year,
        "month2": self.end_date.month,
        "day2": self.end_date.day,
        "format": "comma",
        "latlon": "yes",
        "data": self.data_vars,  # list is handled by doseq
    }
    return f"{BASE_URL}?{urlencode(params, doseq=True)}"

I looked into the IEM ASOS backend docs and found a few missing params worth addressing:

tz (bug risk): The API accepts a tz parameter controlling how observation timestamps are presented. Omitting it is risky: a recently patched server bug caused it to default to America/Chicago instead of UTC when tz was absent. Should explicitly pass tz=Etc/UTC (or whatever timezone is intended) rather than rely on server default.

station: The API supports per-station filtering via repeated station= params (e.g. station=DSM&station=MCW), separate from network. If this class intentionally fetches by network only, that's fine but worth a comment.

report_type: Omitting this returns all report types (MADIS HFMETAR/5-min, routine hourly, and specials) combined. If the intent is clean hourly obs only, this should be set explicitly.

elev: Minor, but since latlon=yes is already set, elev=yes is available if elevation is useful downstream.

The tz omission is the only one that's a correctness issue; the rest are defaults that may or may not match intent.

"""
super().__init__()
self.state = state
self.network = "ASOS" if state.upper() == "ASOS" else f"{state.upper()}_ASOS"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"ASOS" is not a valid IEM network identifier. Global fetch likely requires station=_ALL; clarify intended behavior.

df = pd.read_csv(StringIO(text), skiprows=5)
if df.empty:
logger.warning(f"ASOS returned no data for network {self.network}")
return pd.DataFrame()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty return conflates "no data" with error; caller in _reap cannot distinguish. Consider raising DataNotFoundError or similar instead.

except Exception as e:
raise DateRangeError(f"Could not parse date: {e}") from e

if variable is None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"all" is an IEM magic value; extract as module-level constant (e.g. _IEM_ALL_VARS = "all") so it's not a bare string here and in any future URL building.

class ASOSReaper(TimeSeriesReaper):
"""Reaper for IEM ASOS data."""

timeout: int = 120

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class-level attribute is mutable globally (ASOSReaper.timeout = x affects all instances). Move to __init__ param, if per-instance override is intended, or rename TIMEOUT to signal it's a constant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants