refactor: fire digest — tighter format, 220-byte budget, 7d freshness gate

Query now filters out 100% contained and >7d stale fires. Line format
uses county-only anchor (no Photon geocoder). Greedy 220-byte packing
ensures the digest fits in a single Meshtastic frame.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt Johnson (via Claude) 2026-06-09 05:55:22 +00:00
commit 45ca536140

View file

@ -82,46 +82,45 @@ async def render_digest(*, now: Optional[int] = None) -> tuple[str, str]:
now = now if now is not None else int(time.time()) now = now if now is not None else int(time.time())
conn = get_db() conn = get_db()
fires = conn.execute( rows = conn.execute(
"SELECT incident_name, current_acres, current_contained_pct, " "SELECT incident_name, current_acres, current_contained_pct, county, state "
"lat, lon, county, state " "FROM fires WHERE tombstoned_at IS NULL "
"FROM fires " "AND (current_contained_pct IS NULL OR current_contained_pct < 100) "
"WHERE tombstoned_at IS NULL " "AND last_event_at > strftime('%s', 'now', '-7 days') "
"ORDER BY COALESCE(current_acres, 0) DESC LIMIT 20", "ORDER BY current_acres DESC NULLS LAST LIMIT 20"
).fetchall() ).fetchall()
if not fires: if not rows:
return "", "no_fires" return "", "no_fires"
n = len(fires) n = len(rows)
lines: list[str] = [] header = f"\U0001f525 Fire Digest \u2014 {n} active wildfire(s) in Idaho"
lines.append(f"\U0001f525 Fire Digest \u2014 {n} active wildfire(s) in Idaho")
for f in fires[:5]: fire_lines: list[str] = []
name = f["incident_name"] or "(unnamed)" for row in rows:
acres = f["current_acres"] name = row["incident_name"] or "(unnamed)"
contained = f["current_contained_pct"] anchor = f"{row['county']} Co" if row["county"] else row["state"] or ""
contained = f"{int(row['current_contained_pct'])}% cont" if row["current_contained_pct"] is not None else "uncontained"
acres = f"{int(row['current_acres']):,} ac" if row["current_acres"] else "size unknown"
fire_lines.append(f"{name}: {acres}, {contained}, {anchor}")
acres_str = f"{int(acres):,} ac" if acres and acres > 0 else "size unknown" # 220-byte budget: greedily fit lines
contained_str = (f"{int(contained)}% contained" shown: list[str] = []
if contained is not None else "containment unknown") for line in fire_lines:
remaining = n - len(shown) - 1
overflow = f"\n+ {remaining} more" if remaining > 0 else ""
candidate = "\n".join([header] + shown + [line]) + overflow
if len(candidate.encode("utf-8")) <= 220:
shown.append(line)
else:
break
remaining = n - len(shown)
lines = [header] + shown
if remaining > 0:
lines.append(f"+ {remaining} more")
wire = "\n".join(lines)
anchor = _get_anchor(f["lat"], f["lon"]) return wire, "deterministic"
if not anchor:
county = f["county"]
state = f["state"]
if county and state:
anchor = f"{county} Co {state}"
parts = [f"{name}: {acres_str}, {contained_str}"]
if anchor:
parts[0] += f", {anchor}"
lines.append(parts[0])
if n > 5:
lines.append(f"+ {n - 5} more")
return "\n".join(lines), "deterministic"
# =========================================================================== # ===========================================================================