3 Commits

Author SHA1 Message Date
justin 9b03bb57a8 docs(README): document include_expired on the latest snapshot (#3)
CI / test (push) Successful in 17s
CI / build-push (push) Successful in 6s
2026-06-08 22:36:18 -04:00
justin 831c3e19de latest_prices: include_expired passthrough for historical data (#2)
CI / test (push) Successful in 19s
CI / build-push (push) Successful in 5s
2026-06-08 19:45:03 -04:00
claude bb4219da87 feat: nutrient_cost tool — cheapest fertilizer per lb of N/P/K (#1)
CI / test (push) Successful in 18s
CI / build-push (push) Successful in 5s
Co-authored-by: claude <claude@jpaul.io>
Co-committed-by: claude <claude@jpaul.io>
2026-06-04 15:58:59 -04:00
4 changed files with 43 additions and 5 deletions
+1 -1
View File
@@ -83,7 +83,7 @@ auto-updates it every 5 minutes.
| `price_series(commodity, geo?, start_year?, end_year?)` | Raw monthly price-received series for charting |
| `input_cost_trend(item, geo?, years?)` | Real input cost + change. `item` = `diesel` (U.S. `$/gal`, weekly, EIA) or a fertilizer `$/ton` (`urea`/`uan`/`anhydrous`/`dap`/`map`/`potash`, monthly, USDA AgTransport); `geo` selects the fertilizer region (default `Cornbelt`) |
| `input_cost_series(item, geo?)` | Raw historical series for an input cost (diesel `$/gal` or fertilizer `$/ton`, region-selectable) |
| `latest_prices(commodity?, source?, delivery?, kind?, zip?, lat?, lng?, radius_miles?)` | Live snapshot table; every filter optional (`kind` = `grain`/`fertilizer`). Location filter (zip **or** lat/lng + `radius_miles`) keeps nearby sources, nearest-first, with distance |
| `latest_prices(commodity?, source?, delivery?, kind?, zip?, lat?, lng?, radius_miles?, include_expired?)` | Live snapshot table; every filter optional (`kind` = `grain`/`fertilizer`). Current + future delivery months only by default; pass `include_expired=true` for past months (use only on an explicit historical request). Location filter (zip **or** lat/lng + `radius_miles`) keeps nearby sources, nearest-first, with distance |
| `price_history(commodity?, source?, delivery?, days?)` | Time series per (elevator, crop, delivery); **every filter optional** — omit `commodity` to span all crops. Shows bid + basis trend + futures |
| `basis_movement(commodity?, source?, delivery?, days?)` | Aggregated basis trend, one headline line per crop (the cheap overview) |
| `basis_detail(commodity?, source?, delivery?, days?)` | Per-(elevator, crop, delivery) basis first→last drill-down |
+6 -2
View File
@@ -56,10 +56,14 @@ def _get(path: str, **params: Any) -> dict | list:
def latest(commodity: str | None = None, source: str | None = None,
delivery: str | None = None, kind: str | None = None,
zip: str | None = None, lat: float | None = None,
lng: float | None = None, radius_miles: float | None = None) -> dict:
lng: float | None = None, radius_miles: float | None = None,
include_expired: bool = False) -> dict:
# Only send include_expired when True — _get drops None, so the default
# call keeps its existing query string (current + future months only).
return _get("/api/data/latest",
commodity=commodity, source=source, delivery=delivery, kind=kind,
zip=zip, lat=lat, lng=lng, radius_miles=radius_miles)
zip=zip, lat=lat, lng=lng, radius_miles=radius_miles,
include_expired=True if include_expired else None)
def history(commodity: str | None = None, source_id: int | None = None,
+12 -2
View File
@@ -176,13 +176,22 @@ def latest_prices(
Field(description="Search radius in miles around the location (default 50). "
"Ignored unless a ZIP or lat/lng is given."),
] = None,
include_expired: Annotated[
bool,
Field(description="Include past/expired delivery months. Default off — only "
"the current and future months are shown. Set true only when "
"the farmer explicitly asks for historical/past delivery data."),
] = False,
) -> str:
"""Snapshot of the latest scraped bid per (source, commodity, delivery).
Every filter is optional and AND'd together — pivot by elevator, crop,
delivery, or kind in any combination. Pass a `zip` (or `lat`+`lng`) with
optional `radius_miles` to keep only elevators near the farmer, sorted
nearest-first with the distance shown."""
nearest-first with the distance shown.
By default this shows current + future delivery months only; expired
months are rolled off. Set `include_expired=true` for a historical view."""
cm = commodity.strip().lower() if commodity else None
with track("latest_prices", commodity=cm, source=source, delivery=delivery, kind=kind,
zip=zip or "", radius=radius_miles or 0):
@@ -190,7 +199,8 @@ def latest_prices(
if err:
return err
payload = client.latest(commodity=cm, source=source, delivery=delivery, kind=kind,
zip=zip, lat=lat, lng=lng, radius_miles=radius_miles)
zip=zip, lat=lat, lng=lng, radius_miles=radius_miles,
include_expired=include_expired)
return fmt.fmt_latest(payload)
+24
View File
@@ -71,6 +71,30 @@ def test_get_drops_none_params(monkeypatch):
assert captured["params"] == {"commodity": "corn"}
def test_latest_include_expired_passthrough(monkeypatch):
client = _reload_client(monkeypatch)
captured = {}
class FakeResp:
status_code = 200
text = ""
def json(self): return {"count": 0, "rows": []}
def fake_get(url, params=None, timeout=None, headers=None):
captured["params"] = dict(params or {})
return FakeResp()
monkeypatch.setattr(client.httpx, "get", fake_get)
# Default: not sent, so the API applies its current+future filter.
client.latest(commodity="corn")
assert "include_expired" not in captured["params"]
# Opt in: forwarded so the API returns expired months too.
client.latest(commodity="corn", include_expired=True)
assert captured["params"] == {"commodity": "corn", "include_expired": True}
def test_futures_endpoint_params(monkeypatch):
client = _reload_client(monkeypatch)
captured = {}