Review: ODIN stale events and 0001-01-01 no-ETR sentinel (Refs #184)
ODIN still lists Avangrid rows from April 2026 (ETR April/May) as active on 2026-10-05; parse_odin now counts events whose own ETR passed >24h ago as stale_customers_out, not customers_out, and since/etr come from live events. Puget Sound Energy sends 0001-01-01T00:00:00Z for no ETR; treat as None. Ack dialog now shows a refused enable's error instead of hiding it behind the overlay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ebeabe342e
commit
0efcd676c3
@@ -13,6 +13,7 @@ What is held here (mirrors tests/test_camera_providers.py):
|
||||
"""
|
||||
import asyncio
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
@@ -30,6 +31,7 @@ ADMIN = {"uid": "admin-1", "email": "admin@example.com", "role": "admin"}
|
||||
FIXTURE = json.loads((Path(__file__).parent / "fixtures" / "odin_public_2026-10-05.json").read_text())
|
||||
STUBS = ("poweroutage_us", "coned_oru_stormcenter", "nyseg_rge_stormcenter", "central_hudson_stormcenter", "eagle_i_live")
|
||||
NY_BBOX = (42.5, -78.5, 43.5, -75.5) # Rochester .. Syracuse
|
||||
RECORDED_AT = datetime(2026, 10, 5, 6, 45, tzinfo=timezone.utc) # when the fixture was captured
|
||||
|
||||
|
||||
class FakeStore:
|
||||
@@ -70,7 +72,8 @@ def run(coro):
|
||||
|
||||
|
||||
def _odin_areas():
|
||||
return providers.parse_odin(FIXTURE["odi_format_json"], FIXTURE["odi_map_scale_county"], FIXTURE["odi_status"])[0]
|
||||
return providers.parse_odin(FIXTURE["odi_format_json"], FIXTURE["odi_map_scale_county"], FIXTURE["odi_status"],
|
||||
now=RECORDED_AT)[0]
|
||||
|
||||
|
||||
# ── registry / gate ──────────────────────────────────────────────────────────
|
||||
@@ -244,7 +247,8 @@ def test_outages_route_requires_sign_in():
|
||||
# ── ODIN adapter (recorded public payloads, 2026-10-05) ──────────────────────
|
||||
|
||||
def test_odin_parser_reads_recorded_payload():
|
||||
areas, info = providers.parse_odin(FIXTURE["odi_format_json"], FIXTURE["odi_map_scale_county"], FIXTURE["odi_status"])
|
||||
areas, info = providers.parse_odin(FIXTURE["odi_format_json"], FIXTURE["odi_map_scale_county"], FIXTURE["odi_status"],
|
||||
now=RECORDED_AT)
|
||||
by = {a["fips"]: a for a in areas}
|
||||
assert set(by) == {"36001", "36067", "36055", "53033"}
|
||||
onondaga = by["36067"]
|
||||
@@ -253,7 +257,9 @@ def test_odin_parser_reads_recorded_payload():
|
||||
assert onondaga["customers_out"] == 11 and onondaga["customers_served"] is None
|
||||
assert onondaga["etr"] == "2026-10-05T08:00:00Z" and onondaga["since"] == "2026-10-04T18:35:40Z"
|
||||
assert onondaga["updated_at"].startswith("2026-10-05T06:20:47")
|
||||
assert onondaga["utilities"] == [{"name": "NATIONAL GRID", "eia_id": "13573", "customers_out": 11}]
|
||||
assert onondaga["utilities"] == [{"name": "NATIONAL GRID", "eia_id": "13573", "customers_out": 11,
|
||||
"stale_customers_out": 0}]
|
||||
assert onondaga["stale"] is False and onondaga["stale_customers_out"] == 0
|
||||
s, w, n, e = onondaga["bbox"]
|
||||
assert s < onondaga["lat"] < n and w < onondaga["lon"] < e
|
||||
assert 42.7 < onondaga["lat"] < 43.3 and -76.6 < onondaga["lon"] < -75.8
|
||||
@@ -261,9 +267,27 @@ def test_odin_parser_reads_recorded_payload():
|
||||
king = by["53033"]
|
||||
assert king["customers_out"] == sum(r["metersOut"] for r in FIXTURE["odi_map_scale_county"] if r["id"] == "53033")
|
||||
assert len(king["utilities"]) == 2
|
||||
# Puget Sound Energy's "0001-01-01T00:00:00Z" means no ETR -- not a restoration time, not stale.
|
||||
assert king["etr"] is None and king["stale_customers_out"] == 0
|
||||
assert info["reporting_utilities"] == len(FIXTURE["odi_status"]) and info["utilities_with_error"] == 1
|
||||
|
||||
|
||||
def test_odin_events_long_past_their_etr_are_stale_not_live():
|
||||
# ODIN still listed Avangrid's Monroe County row from April 2026 (ETR
|
||||
# 2026-04-10) on 2026-10-05. It must not read as 18 customers out right now.
|
||||
by = {a["fips"]: a for a in _odin_areas()}
|
||||
monroe = by["36055"]
|
||||
assert monroe["customers_out"] == 0 and monroe["stale_customers_out"] == 18 and monroe["stale"] is True
|
||||
assert monroe["since"].startswith("2026-04-10") # still shown, so the stale row is explainable
|
||||
# Same payload, captured the day the outage was live: not stale.
|
||||
live = {a["fips"]: a for a in providers.parse_odin(
|
||||
FIXTURE["odi_format_json"], FIXTURE["odi_map_scale_county"], FIXTURE["odi_status"],
|
||||
now=datetime(2026, 4, 10, 20, 0, tzinfo=timezone.utc))[0]}
|
||||
assert live["36055"]["customers_out"] == 18 and live["36055"]["stale"] is False
|
||||
# Live counties sort ahead of stale ones.
|
||||
assert [a["stale"] for a in _odin_areas()] == sorted(a["stale"] for a in _odin_areas())
|
||||
|
||||
|
||||
def test_odin_fetch_uses_the_three_public_endpoints():
|
||||
calls = []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user