FlutIQ / app /tools /noaa.py
kredd25's picture
backend: full 7-agent pipeline working end-to-end
4bdad05
"""NOAA NWS API — point forecast and active alerts. Free, no auth (UA required)."""
import httpx
from app.config import USER_AGENT
NWS_HEADERS = {"User-Agent": USER_AGENT, "Accept": "application/geo+json"}
async def get_forecast_and_alerts(lat: float, lon: float) -> dict:
async with httpx.AsyncClient(
timeout=20, headers=NWS_HEADERS, follow_redirects=True
) as client:
# /points returns the grid metadata + forecast URL for the cell.
points_resp = await client.get(
f"https://api.weather.gov/points/{lat},{lon}"
)
if points_resp.status_code == 404:
return {"forecast": None, "alerts": [], "note": "outside CONUS NWS coverage"}
points_resp.raise_for_status()
points = points_resp.json()
forecast_url = (points.get("properties") or {}).get("forecast")
alerts_url = f"https://api.weather.gov/alerts/active?point={lat},{lon}"
forecast_data = None
if forecast_url:
try:
f_resp = await client.get(forecast_url)
if f_resp.status_code == 200:
forecast_data = f_resp.json()
except httpx.HTTPError:
forecast_data = None
try:
a_resp = await client.get(alerts_url)
a_resp.raise_for_status()
alerts = (a_resp.json().get("features") or [])
except httpx.HTTPError:
alerts = []
# Trim forecast down to the next 5 periods to keep prompt small.
periods = []
if forecast_data:
periods = (
forecast_data.get("properties", {}).get("periods") or []
)[:5]
flood_alerts = [
{
"event": (a.get("properties") or {}).get("event"),
"severity": (a.get("properties") or {}).get("severity"),
"headline": (a.get("properties") or {}).get("headline"),
"expires": (a.get("properties") or {}).get("expires"),
}
for a in alerts
if "flood" in ((a.get("properties") or {}).get("event") or "").lower()
or "flood" in ((a.get("properties") or {}).get("headline") or "").lower()
]
return {
"forecast_periods": [
{
"name": p.get("name"),
"temperature": p.get("temperature"),
"wind": p.get("windSpeed"),
"short_forecast": p.get("shortForecast"),
"detailed_forecast": p.get("detailedForecast"),
}
for p in periods
],
"active_flood_alerts": flood_alerts,
"alert_count_total": len(alerts),
}