mirror of
https://github.com/zvx-echo6/central.git
synced 2026-06-10 11:54:37 +02:00
v0.10.10: new avalanche_org adapter — backcountry avalanche advisories (#98)
meshai-requested adapter for avalanche.org's per-center map layers (SNFAC Sawtooth + PAC Payette by default; operator-extensible to any avalanche.org forecast center). Pure passthrough + severity gate, no cross-source fusion, fits Central's adapter pattern cleanly. Adapter surface: - Polls https://api.avalanche.org/v2/public/products/map-layer/{center_id} per configured center; default cadence 1800s (30 min). - Severity gate: only danger_level >= 3 publishes. danger_level 0/1/2 (None/Low/Moderate), -1 ('no rating'), and off_season=true all omitted at adapter level. Idaho summer = all 4 SNFAC + 2 PAC zones yield 0 events; that's correct behavior, verified by the negative-case test against the frozen 2026-06-08 SNFAC fixture. - Severity mapping (corrected from meshai's inverted spec): danger_level 3 (Considerable) → severity 2, 4 (High) → 3, 5 (Extreme) → 4. Matches Central's 4-most-severe convention (nws.SEVERITY_MAP). - Subject: central.avy.advisory.us.{state_lower} — one per state; v0.10.8's category-discriminated Nats-Msg-Id keeps multiple zones in the same state from colliding in JetStream dedup. - Stream: CENTRAL_AVY (central.avy.>); 7-day / 1 GiB retention defaults. - Event.data fields per meshai spec: center_id, zone_name, danger_level, danger_name, travel_advice (truncated to 200 chars), state, valid_date, end_date, off_season=false, latitude/longitude (polygon centroid via shapely), plus geo.geometry passes through as the upstream Polygon. Tests (38 in test_avalanche_org.py): - Pure helpers: _slug (8 cases), _parse_iso (6 cases), _centroid (2 cases). - Severity gate: 3 publish cases (danger 3/4/5 → severity 2/3/4), 4 omit cases (danger -1/0/1/2), off_season=true omit, missing state omit, unparseable geom omit, travel_advice truncation, subject derivation. - Real-fixture negative case: 4-zone SNFAC fixture all omitted off-season. - Real-fixture positive case: same fixture with synthetic winter overrides publishes all 4 with valid centroids on actual Idaho polygons. - End-to-end poll() with mixed severities and the new wiring (streams registry + supervisor family map). - Defensive: empty center_ids list yields nothing without crashing. Wiring + plumbing: - src/central/streams.py: StreamEntry('CENTRAL_AVY', 'central.avy.>') - src/central/supervisor.py: STREAM_CATEGORY_DOMAINS['CENTRAL_AVY']=('avy',) - sql/migrations/035: seed config.streams row (mirror of 019/CENTRAL_SPACE, idempotent ON CONFLICT DO NOTHING). Note: migrations don't auto-run on supervisor restart -- see deferred ops list (schema_migrations cleanup blocks central-migrate from running anything cleanly). - src/central/gui/templates/_event_rows/avalanche_org.html (8 lines) - src/central/gui/templates/_event_summaries/avalanche_org.html (2 lines) Both required by the existing per-adapter template consistency tests. Doc updates (required by existing doc-vs-registry tests): - docs/PRODUCER-INTEGRATION.md §6.1: added 'avy' to top-level-domain list. - docs/PRODUCER-INTEGRATION.md §8: added StreamEntry('CENTRAL_AVY',...) line to the verbatim snippet. - docs/CONSUMER-INTEGRATION.md §3 stream layout table: added CENTRAL_AVY row. - docs/CONSUMER-INTEGRATION.md §6: new '### avalanche_org' subsection with source, subject convention, dedup key, severity gate, Event.data field table, and off-season behavior note. - tests/test_events_feed_frontend.py: added avalanche_org to _SAMPLE_INNER and _EXPECTED_SUBJECT (the events-JSON subject-derivation coverage tests). Budget note: this PR is well over the ~400-line target -- the new-adapter surface picked up downstream consistency tests (doc validators + frontend sample coverage + template partials) I didn't anticipate at probe time. Most of the overrun is the SNFAC fixture (1,135 lines pretty-printed JSON, non-code) and the adapter + tests pair. Stripping the fixture and the required doc/template edits would leave ~620 lines of code; the fixture itself is a frozen snapshot, not a maintenance burden. Full sweep: 1072 passed, 0 failures (+41 from this PR), ruff clean on all new files. One PRE-EXISTING ruff violation in supervisor.py (unused poll_start variable at line 388) surfaces when we touch supervisor.py; confirmed not introduced by this PR via git stash check. Deploy plan (NEW STREAM — archive restart required per [[feedback_new_stream_needs_archive_restart]]): 1. Squash-merge -> tag v0.10.10 -> push. 2. On central: pull main -> systemctl restart central-supervisor -> ALSO systemctl restart central-archive (new event-bearing stream; archive enumerates consumers at startup and doesn't hot-reload). 3. Migration 035 deferred to morning per the schema_migrations cleanup task -- the stream creation itself doesn't depend on it (supervisor creates JetStream streams from the STREAMS registry at startup; the config.streams row is for operator-tunable retention only). 4. Verify: nats stream info CENTRAL_AVY (created), poll log shows yielded=0 / omitted=N (off-season), no positive publishes during summer (correct). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6cbb9378d6
commit
e92b51c518
11 changed files with 1823 additions and 1 deletions
|
|
@ -134,6 +134,7 @@ Central's archive.
|
|||
| `CENTRAL_TRAFFIC` | `central.traffic.>` | 7 | 1 GiB | ✓ | ✓ |
|
||||
| `CENTRAL_TRAFFIC_FLOW` | `central.traffic_flow.>` | 7 | 1 GiB | ✓ | ✓ |
|
||||
| `CENTRAL_TRAFFIC_CAMERAS` | `central.traffic_cameras.>` | 7 | 1 GiB | ✓ | ✓ |
|
||||
| `CENTRAL_AVY` | `central.avy.>` | 7 | 1 GiB | ✓ | ✓ |
|
||||
| `CENTRAL_META` | `central.meta.>` | 1 | 1 GiB | — | ✓ |
|
||||
|
||||
Retention and storage caps are migration-seeded defaults visible in `config.streams`;
|
||||
|
|
@ -1791,6 +1792,45 @@ at parameter `00060`, gage height (ft) at `00065`, water temperature (°C) at
|
|||
\
|
||||
---
|
||||
|
||||
### avalanche_org — avalanche.org backcountry advisories (v0.10.10)
|
||||
|
||||
- **Source:** `https://api.avalanche.org/v2/public/products/map-layer/<center_id>`
|
||||
per configured forecast center (defaults: SNFAC Sawtooth, PAC Payette).
|
||||
- **Stream:** `CENTRAL_AVY` (`central.avy.>`)
|
||||
- **Subject:** `central.avy.advisory.us.<state_lower>` — one subject per state;
|
||||
multiple zones in the same state coexist via category-discriminated
|
||||
`Nats-Msg-Id` (v0.10.8).
|
||||
- **Dedup key shape:** `<center_id>_<zone_name_slug>` (e.g. `SNFAC_banner_summit`).
|
||||
Slug is the zone name lowercased with non-alphanumeric runs collapsed to `_`.
|
||||
- **Severity gate (adapter-side):** only `danger_level >= 3` publishes.
|
||||
`danger_level` of 0/1/2 (None/Low/Moderate), -1 ("no rating"), and
|
||||
`off_season=true` are all omitted — no Event yielded.
|
||||
- **Severity mapping (danger_level → Event.severity / centralseverity):**
|
||||
`3 (Considerable) → 2`, `4 (High) → 3`, `5 (Extreme) → 4`. Higher = more
|
||||
severe per Central convention.
|
||||
- **Event.data fields:**
|
||||
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| `center_id` | string | Upstream forecast center identifier (e.g. `SNFAC`) |
|
||||
| `zone_name` | string | Human-readable zone name (e.g. `Banner Summit`) |
|
||||
| `danger_level` | int | 3, 4, or 5 (published levels only) |
|
||||
| `danger_name` | string | Upstream textual label (`Considerable`/`High`/`Extreme`) |
|
||||
| `travel_advice` | string | Truncated to 200 chars |
|
||||
| `state` | string | 2-letter state code (uppercase) |
|
||||
| `valid_date` | string | Upstream `start_date` ISO string (timezone-naive) |
|
||||
| `end_date` | string | Upstream `end_date` ISO string |
|
||||
| `off_season` | bool | Always `false` for published events |
|
||||
| `latitude` / `longitude` | float | Polygon centroid (computed via shapely) |
|
||||
- **Geometry:** the upstream Polygon passes through as-is in `geo.geometry`.
|
||||
MultiPolygon also supported defensively; centroid is computed from whichever.
|
||||
- **Off-season behavior:** during summer all SNFAC/PAC zones return
|
||||
`off_season=true` + `danger_level=-1` — the adapter yields zero events,
|
||||
by design.
|
||||
|
||||
\
|
||||
---
|
||||
|
||||
## 7. Fall-off / removal semantics
|
||||
|
||||
Central adapters fall into three buckets for handling upstream events that
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue