feat(2-F): NASA EONET disaster adapter

Adds the NASA Earth Observatory Natural Event Tracker (EONET v3) adapter,
publishing on the existing CENTRAL_DISASTER stream under
central.disaster.eonet.<category>.global subjects.

- One Central event per EONET event id; geo = most-recent geometry point.
- Composite dedup key (eonet:<id>:<latest_geometry_date_iso>) — timeline
  advance re-publishes, idle re-poll suppresses.
- category_allowlist defaults to all 13 upstream categories; operator opts
  OUT per-category if GDACS overlap (wildfires/floods/severeStorms/volcanoes)
  produces unwanted dupes on gdacs.* subjects.
- camelCase upstream IDs (seaLakeIce, dustHaze, etc.) mapped to
  lower_snake_case subject components by a single _subject_category helper.
- Country resolves to literal 'global' (no reverse-geocode in v1).
- Fall-off: missing-from-feed event emits central.disaster.eonet.<cat>.removed.global,
  subtype before 'removed' per §8 canonical pattern.

Adapter ships disabled; operator enables via GUI.
This commit is contained in:
zvx 2026-05-19 15:35:25 +00:00
commit 0b26bf902a
4 changed files with 815 additions and 0 deletions

View file

@ -0,0 +1,21 @@
-- Migration: 022_add_eonet_adapter
-- Adds the NASA EONET adapter row to config.adapters.
-- Ships disabled; operator enables via GUI.
--
-- The settings JSON below is the literal output of EONETSettings().model_dump_json()
-- at migration-author time. Regenerate via:
-- sudo -u central .venv/bin/python -c \
-- "from central.adapters.eonet import EONETSettings; print(EONETSettings().model_dump_json())"
-- Do NOT hand-edit the category_allowlist here — _DEFAULT_CATEGORIES in
-- src/central/adapters/eonet.py is the single source of truth.
--
-- Idempotent: uses ON CONFLICT DO NOTHING.
INSERT INTO config.adapters (name, enabled, cadence_s, settings)
VALUES (
'eonet',
false,
1800,
'{"category_allowlist":["drought","dustHaze","earthquakes","floods","landslides","manmade","seaLakeIce","severeStorms","snow","tempExtremes","volcanoes","waterColor","wildfires"],"region":null}'::jsonb
)
ON CONFLICT (name) DO NOTHING;