feat(schema): add adapter column to events, drop source

Replaces module-path-based source column (e.g. "central/adapters/nws")
with stable adapter identifier (e.g. "nws") that foreign-keys to
config.adapters.name.

Migration 011:
- ADD COLUMN adapter TEXT
- Backfill via REPLACE(source, 'central/adapters/', '')
- SET NOT NULL + FK RESTRICT
- CREATE INDEX (adapter, received DESC) for dashboard queries
- DROP COLUMN source

Code changes:
- Event model: source field renamed to adapter
- All adapters: use adapter="name" instead of source="central/adapters/name"
- Archive: write adapter column instead of source

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Ubuntu 2026-05-17 16:07:35 +00:00
commit 8601a19f60
10 changed files with 150 additions and 18 deletions

View file

@ -330,7 +330,7 @@ class FIRMSAdapter(SourceAdapter):
return Event(
id=stable_id,
source="central/adapters/firms",
adapter="firms",
category=f"fire.hotspot.{satellite_short}.{confidence}",
time=time,
expires=None,

View file

@ -475,7 +475,7 @@ class NWSAdapter(SourceAdapter):
return Event(
id=event_id,
source="central/adapters/nws",
adapter="nws",
category=category,
time=time,
expires=expires,

View file

@ -348,7 +348,7 @@ class USGSQuakeAdapter(SourceAdapter):
return Event(
id=event_id,
source="central/adapters/usgs_quake",
adapter="usgs_quake",
category=f"quake.event.{tier}",
time=event_time,
expires=None,

View file

@ -157,7 +157,7 @@ class ArchiveConsumer:
geo_data = event_data.get("geo")
event_id = envelope.get("id")
source = event_data.get("source", "")
adapter = event_data.get("adapter", "")
category = event_data.get("category", "")
time_str = event_data.get("time")
expires_str = event_data.get("expires")
@ -194,12 +194,12 @@ class ArchiveConsumer:
if geom_json:
await conn.execute(
"""
INSERT INTO events (id, source, category, time, expires, severity,
INSERT INTO events (id, adapter, category, time, expires, severity,
geom, regions, primary_region, payload)
VALUES ($1, $2, $3, $4, $5, $6,
ST_GeomFromGeoJSON($7), $8, $9, $10)
ON CONFLICT (id, time) DO UPDATE SET
source = EXCLUDED.source,
adapter = EXCLUDED.adapter,
category = EXCLUDED.category,
expires = EXCLUDED.expires,
severity = EXCLUDED.severity,
@ -208,17 +208,17 @@ class ArchiveConsumer:
primary_region = EXCLUDED.primary_region,
payload = EXCLUDED.payload
""",
event_id, source, category, event_time, expires_time, severity,
event_id, adapter, category, event_time, expires_time, severity,
geom_json, regions, primary_region, json.dumps(envelope)
)
else:
await conn.execute(
"""
INSERT INTO events (id, source, category, time, expires, severity,
INSERT INTO events (id, adapter, category, time, expires, severity,
geom, regions, primary_region, payload)
VALUES ($1, $2, $3, $4, $5, $6, NULL, $7, $8, $9)
ON CONFLICT (id, time) DO UPDATE SET
source = EXCLUDED.source,
adapter = EXCLUDED.adapter,
category = EXCLUDED.category,
expires = EXCLUDED.expires,
severity = EXCLUDED.severity,
@ -227,7 +227,7 @@ class ArchiveConsumer:
primary_region = EXCLUDED.primary_region,
payload = EXCLUDED.payload
""",
event_id, source, category, event_time, expires_time, severity,
event_id, adapter, category, event_time, expires_time, severity,
regions, primary_region, json.dumps(envelope)
)

View file

@ -23,7 +23,7 @@ class Event(BaseModel):
model_config = ConfigDict(extra="forbid", frozen=True)
id: str # unique, stable across republish
source: str # adapter identity, e.g. "central/adapters/nws"
adapter: str # adapter identity, e.g. "nws"
category: str # e.g. "wx.alert.severe_thunderstorm_warning" or "fire.hotspot.viirs_snpp.high"
time: datetime # event-time UTC, not processing-time
expires: datetime | None = None