Skip to content

PR #363 — fix(commodity_hindcast): unbreak Streamlit dashboard startup

At a glance

  • Author: ai-tommytf
  • Merged: 2026-05-05
  • Branch: fix/commodity-hindcast-app-startup
  • Net effect: Three independent bug fixes that together let the commodity_hindcast Streamlit dashboard start against an existing run on disk. All changes are inside commodity_hindcast/app/ and do not affect the modelling pipeline.
  • Why this matters: The dashboard had silently broken after the reference-data refactor in PR-360. This PR restores dashboard functionality and migrates _eval_shim.py to the new WasdeLoader API.

PR body (faithful extract)

## What this PR does

Three small, independent bug fixes that together let the **commodity_hindcast Streamlit dashboard**
start up against an existing run on disk.

| # | File | Symptom | Fix |
|---|------|---------|-----|
| 1 | `app/_dashboard_config.py` | `FileNotFoundError: …/src/configs/corn_experiment.yaml` | One `.parent` too many in the path computation. |
| 2 | `app/_eval_shim.py` | `ImportError: cannot import name 'load_wasde_yield_releases_by_harvest_year'` | Free function was refactored into a class — port the shim to the new `WasdeLoader` API. |
| 3 | `app/_eval_shim.py` | `FileNotFoundError: …/wasde/wasde_wheat_us_yield.csv` | Path missing the `data/` prefix that every commodity YAML uses. |

Fix 1 — _CONFIGS_DIR resolution

The old code used three .parent hops from _dashboard_config.py:

# before
_CONFIGS_DIR = Path(__file__).resolve().parent.parent.parent / "configs"

The file sits at commodity_hindcast/app/_dashboard_config.py. Three .parent hops reach market_insights_models/src/, not commodity_hindcast/. The fix drops one .parent:

_CONFIGS_DIR = Path(__file__).resolve().parent.parent / "configs"

Fix 2 — WASDE loader API migration

_eval_shim.py imported a deleted free function. The new shape uses WasdeRefSpec + WasdeLoader:

spec = WasdeRefSpec(
    name="wasde_jan",
    filepath=AnyPath(str(_wasde_csv_path(commodity))),
    commodity=commodity,
    geography="united_states",
    cutoff_month_day=MonthDay(month=2, day=1),
    unit="bu_acre",
)
df = WasdeLoader(spec, commodity_cfg).load()

The new loader always emits kg/ha; the shim converts back to bu/ac using kg_ha_to_bu_acre_series with the commodity's bushel_weight_lbs.

Fix 3 — data/ prefix on the WASDE CSV path

# before
return require_input_data_dir() / "wasde" / f"wasde_{commodity}_us_yield.csv"
# after
return require_input_data_dir() / "data" / "wasde" / f"wasde_{commodity}_us_yield.csv"

This matches every commodity YAML (reference_data.filepath: data/wasde/wasde_<commodity>_us_yield.csv); the shim was the only caller that had drifted.

Verification

After the three fixes:

curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://localhost:8501/
# HTTP 200

Files / lines touched

Additions Deletions File
+27 -8 market_insights_models/src/commodity_hindcast/app/_eval_shim.py
+1 -1 market_insights_models/src/commodity_hindcast/app/_dashboard_config.py

Cross-references

  • Related entity pages: WasdeLoader (page not yet written), CommodityConfig
  • Related concept pages: unit conventions
  • Related PR: PR-360 (the reference-data refactor that broke the dashboard)
  • Related PR: PR-340 (dashboard feature work that predated the break)

Lessons captured

  • _CONFIGS_DIR in _dashboard_config.py is two .parent hops from the file, not three; the package tree is commodity_hindcast/app/<file>, so parent.parent lands at commodity_hindcast/.
  • The dashboard imports load_wasde_yield_releases_by_harvest_year which was deleted in the CB-1 refactor; any shim using that symbol must be migrated to WasdeLoader.
  • WasdeLoader.load() always returns kg/ha; a kg_ha_to_bu_acre_series round-trip is required before the dashboard's bu/ac chart calculations.
  • The data/ prefix is mandatory in all WASDE CSV paths when INPUT_DATA_DIR is the repo root; require_input_data_dir() / "data" / "wasde" / ... is the canonical form.
  • The dashboard is exercised only by a manual streamlit run; regressions in app/ are invisible until a developer tries to load a run.