- Introduce AlertsNormalizer to convert Raw NWS Alerts (SchemaRawNWSAlertsV1) into canonical WeatherAlert runs (SchemaWeatherAlertV1) - Add minimal NWS alerts response/types to support GeoJSON FeatureCollection parsing - Map NWS alert properties (event, headline, severity, timing, area, references) into model.WeatherAlert with best-effort timestamp handling - Establish clear AsOf / EffectiveAt policy for alert runs to support stable deduplication and snapshot semantics - Register the new alerts normalizer alongside existing NWS observation and forecast normalizers
79 lines
3.2 KiB
Go
79 lines
3.2 KiB
Go
// FILE: internal/model/alert.go
|
||
package model
|
||
|
||
import "time"
|
||
|
||
// WeatherAlertRun is a snapshot of *active* alerts for a location as-of a point in time.
|
||
//
|
||
// This mirrors WeatherForecastRun's "one issued snapshot -> many contained items" shape:
|
||
//
|
||
// - A single run may contain zero, one, or many alerts.
|
||
// - Runs are intended to be immutable snapshots (“provider asserted X at AsOf”).
|
||
//
|
||
// Normalizers should prefer to set AsOf from a provider-supplied “updated/generated” timestamp.
|
||
// If unavailable, AsOf may be set to the poll/emit time as a fallback.
|
||
type WeatherAlertRun struct {
|
||
// Optional location metadata (provider-dependent).
|
||
LocationID string `json:"locationId,omitempty"`
|
||
LocationName string `json:"locationName,omitempty"`
|
||
|
||
// AsOf is when the provider asserted this alert snapshot is current (required).
|
||
AsOf time.Time `json:"asOf"`
|
||
|
||
// Optional spatial context.
|
||
Latitude *float64 `json:"latitude,omitempty"`
|
||
Longitude *float64 `json:"longitude,omitempty"`
|
||
|
||
// Active alerts contained in this snapshot (order is provider-dependent).
|
||
Alerts []WeatherAlert `json:"alerts"`
|
||
}
|
||
|
||
// WeatherAlert is the canonical representation of a single alert.
|
||
//
|
||
// This is intentionally a “useful subset” of rich provider payloads.
|
||
// Normalizers may populate ProviderExtras for structured provider-specific fields
|
||
// that don’t cleanly fit the canonical shape.
|
||
type WeatherAlert struct {
|
||
// Provider-stable identifier (often a URL/URI).
|
||
ID string `json:"id"`
|
||
|
||
// Classification / headline fields.
|
||
Event string `json:"event,omitempty"`
|
||
Headline string `json:"headline,omitempty"`
|
||
|
||
Severity string `json:"severity,omitempty"` // e.g. Extreme/Severe/Moderate/Minor/Unknown
|
||
Urgency string `json:"urgency,omitempty"` // e.g. Immediate/Expected/Future/Past/Unknown
|
||
Certainty string `json:"certainty,omitempty"` // e.g. Observed/Likely/Possible/Unlikely/Unknown
|
||
|
||
Status string `json:"status,omitempty"` // e.g. Actual/Exercise/Test/System/Unknown
|
||
MessageType string `json:"messageType,omitempty"` // e.g. Alert/Update/Cancel
|
||
Category string `json:"category,omitempty"` // e.g. Met/Geo/Safety/Rescue/Fire/Health/Env/Transport/Infra/CBRNE/Other
|
||
Response string `json:"response,omitempty"` // e.g. Shelter/Evacuate/Prepare/Execute/Avoid/Monitor/Assess/AllClear/None
|
||
|
||
// Narrative.
|
||
Description string `json:"description,omitempty"`
|
||
Instruction string `json:"instruction,omitempty"`
|
||
|
||
// Timing (all optional; provider-dependent).
|
||
Sent *time.Time `json:"sent,omitempty"`
|
||
Effective *time.Time `json:"effective,omitempty"`
|
||
Onset *time.Time `json:"onset,omitempty"`
|
||
Expires *time.Time `json:"expires,omitempty"`
|
||
|
||
// Scope / affected area.
|
||
AreaDescription string `json:"areaDescription,omitempty"` // often a provider string
|
||
|
||
// Provenance.
|
||
SenderName string `json:"senderName,omitempty"`
|
||
|
||
References []AlertReference `json:"references,omitempty"`
|
||
}
|
||
|
||
// AlertReference is a reference to a related alert (updates, replacements, etc.).
|
||
type AlertReference struct {
|
||
ID string `json:"id,omitempty"` // provider reference ID/URI
|
||
Identifier string `json:"identifier,omitempty"` // provider identifier string, if distinct
|
||
Sender string `json:"sender,omitempty"`
|
||
Sent *time.Time `json:"sent,omitempty"`
|
||
}
|