v0.9.15: live JS quota recompute on multi-bbox editor

Mirror TomTomIncidentsAdapter.quota_estimate in client-side JS so the
quota panel updates live as the operator edits a per-row cadence or the
adapter cadence, and on Add/Delete bbox -- instead of only on Save.

- tomtom_incidents.py: expose seconds_per_month + default_cadence_s in
  the quota dict (no JS magic numbers).
- model_list.html: data-quota-* attrs + .quota-detail/.quota-msg spans;
  self-contained DOMContentLoaded-guarded IIFE applying the same
  max(adapter_cadence, bbox_cadence||default) floor and 80%/100%
  warn/block thresholds. No-op when no quota panel present.
- tests: structural asserts for data-attrs + span hooks (JS exec needs
  a browser; live behaviour eyeballed manually).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Johnson 2026-05-27 04:29:32 +00:00
commit f52e545ddf
3 changed files with 91 additions and 4 deletions

View file

@ -56,6 +56,27 @@ class TestModelListRender:
assert "model-list" in out and 'name="bboxes-0-name"' in out
assert "free tier" in out # quota panel rendered
def test_quota_panel_exposes_client_recompute_constants(self):
# v0.9.15: data-quota-* attrs let the client JS mirror the server
# formula with no hardcoded magic numbers.
fields = describe_fields(TomTomIncidentsAdapter.settings_schema, _SETTINGS)
quota = TomTomIncidentsAdapter.quota_estimate(TomTomIncidentsSettings(**_SETTINGS), 1800)
out = _render("adapters_edit.html", _ctx(_SETTINGS, fields, quota))
assert 'data-quota-cap="2500"' in out
assert 'data-quota-spm="2592000"' in out
assert 'data-quota-default="1800"' in out
def test_quota_panel_wraps_detail_and_msg_for_live_update(self):
# v0.9.15: detail/msg split into spans so the IIFE can rewrite the text
# and warn/block state live. JS exec needs a browser, so this is
# structural only -- live behaviour (cadence edit + Add/Delete row)
# was eyeballed manually against /adapters/tomtom_incidents.
fields = describe_fields(TomTomIncidentsAdapter.settings_schema, _SETTINGS)
quota = TomTomIncidentsAdapter.quota_estimate(TomTomIncidentsSettings(**_SETTINGS), 1800)
out = _render("adapters_edit.html", _ctx(_SETTINGS, fields, quota))
assert '<span class="quota-detail">' in out
assert '<span class="quota-msg">' in out
def test_nws_region_intact_no_model_list(self):
from central.adapters.nws import NWSSettings
s = {"contact_email": "a@b.com",