Commit graph

2 commits

Author SHA1 Message Date
zvx
570b121276 fix(2-G.5): preview_for_settings contract in adapter docstring + distinguish [] from None
Fixup 1 — Contract section appended to SourceAdapter.preview_for_settings's
docstring. Override authors read adapter.py, not routes.py, so the contract
(pure function of settings; open your own short-lived aiohttp session; None
vs [] semantics) belongs on the base method, not on the GUI stub class.

Fixup 2 — _adapter_preview.html distinguishes [] from None. Previously the
elif test was truthiness (`elif preview_rows`) which collapsed both into
"render nothing". Now uses `elif preview_rows is not none` and special-cases
the empty-list case inside: legend "Preview (0 rows)" with no table; None
still renders nothing at all. Lets adapters signal "query ran, matched zero"
distinctly from "preview not meaningful".

Tests +1:
- test_partial_renders_empty_list — [] yields "Preview (0 rows)" legend,
  no table, no headers. Distinct from the existing None case.

Acceptance:
- 27/27 targeted (preview_hook +1 new, nwis, stream_registry).
- 458/458 full suite.
- (b) framework GUI dir still has zero adapter-name branches.
2026-05-19 17:55:39 +00:00
zvx
ead6ef8ce1 feat(2-G.5): preview_for_settings framework hook + NWIS opt-in
Adds an optional async hook on SourceAdapter so any adapter can surface a
settings-driven preview on its /adapters/<name> edit page. The framework
renders the result generically as a table — no adapter-name branches in
GUI templates or route code.

Framework changes:
- src/central/adapter.py: new async preview_for_settings(self, settings)
  on the base class, default returns None. Adapters opt in by overriding;
  non-overriding adapters render unchanged.
- src/central/gui/routes.py: GET /adapters/{name} instantiates the adapter
  with a no-op _PreviewConfigStore stub and a /dev/null cursor path (GUI
  has no live ConfigStore), constructs settings_obj via the schema, and
  calls preview_for_settings inside a try/except. Result lands in template
  context as preview_rows / preview_error.
- src/central/gui/templates/_adapter_preview.html: new partial. Generic
  table with columns derived from the first dict's keys; error banner
  mirrors the existing last_error article style.
- src/central/gui/templates/adapters_edit.html: one-line include between
  the Region fieldset and Save/Cancel.

NWIS opt-in:
- New NWIS_MONITORING_LOCATIONS_URL constant and _PREVIEW_LIMIT cap of 50.
- preview_for_settings returns None when region is None, otherwise one-shot
  fetches monitoring-locations within the bbox via a fresh aiohttp session.
  Must work even when adapter is not started -- the GUI process never calls
  startup(). Returns list[dict] with the contract column order: site_id,
  name, site_type, state. Errors propagate so the framework can render the
  operator-visible banner.
- HTTP call factored into _fetch_preview_text so tests mock cleanly.

Tests (7 new):
- tests/test_preview_hook.py: default returns None; partial renders list
  with correct headers/rows/count; partial renders error banner; partial
  renders empty when both context values are None.
- tests/test_nwis.py adds TestNWISPreview: returns None without region,
  returns rows with correct column order, propagates HTTP errors.

Verification:
- 457/457 full suite green (was 450; +7 new tests).
- Live /adapters/nwis preview returns 50 rows with the contract keys
  against the current production Iowa bbox.
- /adapters/eonet preview_for_settings returns None via base default --
  proves framework is duck-typed, no NWIS-specific code in framework.
2026-05-19 17:34:35 +00:00