mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-08-26 09:21:33 +00:00
fix(reminders): resolve Active: fire location via _fire_anchor, not county/state
ReminderScheduler._render built the wfigs "Active:" reminder location as a
bare " / ".join(county, state), skipping the anchor resolution (geocoder_city
-> curated town_anchors -> Photon -> landclass -> county -> state) that the
New/Update fire formatter (formatters/fire.py::_fire_anchor) already uses.
Reminders now call the same _fire_anchor helper on dict(row) (sqlite3.Row has
no .get()) and fit the result to fit_to_budget(..., budget_for("wfigs")),
matching how the other renderers guard the mesh packet budget.
This commit is contained in:
parent
522458f194
commit
2eddd9b572
2 changed files with 97 additions and 2 deletions
|
|
@ -59,6 +59,9 @@ import time
|
|||
from datetime import datetime, timezone
|
||||
from typing import Any, Optional
|
||||
|
||||
from meshai.notifications.formatters._budget import budget_for, fit_to_budget
|
||||
from meshai.notifications.formatters.fire import _fire_anchor
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
@ -370,8 +373,9 @@ class ReminderScheduler:
|
|||
cont = row["current_contained_pct"]
|
||||
acres_s = "N/A" if acres is None else f"{int(acres):,} ac"
|
||||
cont_s = "?" if cont is None else f"{int(cont)}%"
|
||||
anchor = " / ".join(p for p in (row["county"], row["state"]) if p) or "?"
|
||||
return f"🔥 {prefix}: {name}, {anchor}: {acres_s}, {cont_s} contained"
|
||||
anchor = _fire_anchor(dict(row))
|
||||
wire = f"🔥 {prefix}: {name}, {anchor}: {acres_s}, {cont_s} contained"
|
||||
return fit_to_budget(wire, budget_for("wfigs"))
|
||||
if adapter == "swpc":
|
||||
return f"🌌 {prefix}: ongoing space weather event ({row['event_type']})"
|
||||
if adapter == "itd_511_work_zone":
|
||||
|
|
|
|||
|
|
@ -229,3 +229,94 @@ def test_work_zone_reminder_skipped_outside_slot(mock_dispatcher):
|
|||
sch = ReminderScheduler(mock_dispatcher, clock=lambda: now, tick_seconds=60)
|
||||
fired = asyncio.run(sch.tick_once())
|
||||
assert fired == 0
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Anchor resolution in the wfigs "Active:" reminder wire (_fire_anchor)
|
||||
# ============================================================================
|
||||
|
||||
|
||||
def test_wfigs_reminder_uses_anchor_not_county(mock_dispatcher):
|
||||
"""A fire near a known curated town_anchors entry (Bliss, ID) renders the
|
||||
anchor phrase ('mi <bearing> of Bliss'), not the bare county/state join
|
||||
the old renderer produced."""
|
||||
now = 1_780_000_000
|
||||
conn = get_db()
|
||||
_enable_wfigs_reminders()
|
||||
# Coordinates ~4 mi NW of Bliss, ID (see _TOWN_ANCHORS_SEED in
|
||||
# meshai/persistence/curation.py) -- county/state deliberately different
|
||||
# from "Bliss" so the assertion can't pass by accident via the county.
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO fires(irwin_id, incident_name, incident_type, "
|
||||
"current_acres, current_contained_pct, lat, lon, county, state, "
|
||||
"declared_at, last_event_at, first_broadcast_at, last_broadcast_at) "
|
||||
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
("F-ANCHOR", "Jackalope", "WF", 15, 0,
|
||||
42.98, -114.99, "Gooding", "ID",
|
||||
now - 9 * 3600, now, now - 9 * 3600, now - 9 * 3600),
|
||||
)
|
||||
|
||||
sch = ReminderScheduler(mock_dispatcher, clock=lambda: now)
|
||||
fired = asyncio.run(sch.tick_once())
|
||||
assert fired == 1
|
||||
args = mock_dispatcher.dispatch_scheduled_fire_broadcast.call_args.kwargs
|
||||
text = args["text"]
|
||||
assert "Bliss" in text
|
||||
assert "of Bliss" in text
|
||||
# Old format ("Gooding / ID") must be gone.
|
||||
assert "Gooding / ID" not in text
|
||||
|
||||
|
||||
def test_wfigs_reminder_falls_back_when_no_anchor_nearby(mock_dispatcher):
|
||||
"""A fire with no lat/lon at all still renders sensibly via
|
||||
_fire_anchor's fallback chain (county -> state -> "location unknown"),
|
||||
and never raises."""
|
||||
now = 1_780_000_000
|
||||
conn = get_db()
|
||||
_enable_wfigs_reminders()
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO fires(irwin_id, incident_name, incident_type, "
|
||||
"current_acres, current_contained_pct, lat, lon, county, state, "
|
||||
"declared_at, last_event_at, first_broadcast_at, last_broadcast_at) "
|
||||
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
("F-NOANCHOR", "Remote Fire", "WF", 42, 5,
|
||||
None, None, "Owyhee", "ID",
|
||||
now - 9 * 3600, now, now - 9 * 3600, now - 9 * 3600),
|
||||
)
|
||||
|
||||
sch = ReminderScheduler(mock_dispatcher, clock=lambda: now)
|
||||
fired = asyncio.run(sch.tick_once())
|
||||
assert fired == 1
|
||||
args = mock_dispatcher.dispatch_scheduled_fire_broadcast.call_args.kwargs
|
||||
text = args["text"]
|
||||
# Falls through landclass (absent) to the "{county} Co {state}" tier.
|
||||
assert "Owyhee Co ID" in text
|
||||
assert "Active" in text
|
||||
|
||||
|
||||
def test_wfigs_reminder_truncates_to_budget(mock_dispatcher):
|
||||
"""A very long incident name + anchor is truncated to the wfigs budget
|
||||
(fit_to_budget) rather than exceeding the mesh packet limit."""
|
||||
now = 1_780_000_000
|
||||
conn = get_db()
|
||||
_enable_wfigs_reminders()
|
||||
long_name = ("The Extremely Long Wildfire Incident Name That Keeps Going "
|
||||
"And Going And Going Well Past Any Reasonable Mesh Packet Budget")
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO fires(irwin_id, incident_name, incident_type, "
|
||||
"current_acres, current_contained_pct, lat, lon, county, state, "
|
||||
"declared_at, last_event_at, first_broadcast_at, last_broadcast_at) "
|
||||
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
("F-LONG", long_name, "WF", 123456, 3,
|
||||
42.98, -114.99, "Gooding", "ID",
|
||||
now - 9 * 3600, now, now - 9 * 3600, now - 9 * 3600),
|
||||
)
|
||||
|
||||
from meshai.notifications.formatters._budget import budget_for
|
||||
sch = ReminderScheduler(mock_dispatcher, clock=lambda: now)
|
||||
fired = asyncio.run(sch.tick_once())
|
||||
assert fired == 1
|
||||
args = mock_dispatcher.dispatch_scheduled_fire_broadcast.call_args.kwargs
|
||||
text = args["text"]
|
||||
assert len(text) <= budget_for("wfigs")
|
||||
assert text.endswith("…")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue