Implement alert instruction whitespace normalization

This commit is contained in:
2026-06-16 21:19:03 -05:00
parent 6532e8824a
commit 5d416cfc4a
2 changed files with 30 additions and 3 deletions

View File

@@ -492,7 +492,7 @@ func parseAlert(raw json.RawMessage) (parsedAlert, bool) {
Headline: firstStringField(fields, "headline", "title"),
Severity: stringField(fields, "severity"),
Description: stringField(fields, "description"),
Instruction: stringField(fields, "instruction"),
Instruction: collapseWhitespace(stringField(fields, "instruction")),
}
start, startOK := firstTimeField(fields, "onset", "startsAt", "startTime", "effective", "sent")
end, endOK := firstTimeField(fields, "ends", "endsAt", "endTime", "expires")
@@ -542,6 +542,10 @@ func firstTimeField(fields map[string]json.RawMessage, names ...string) (time.Ti
return time.Time{}, false
}
func collapseWhitespace(value string) string {
return strings.Join(strings.Fields(value), " ")
}
func intersect(left timeutil.Period, right timeutil.Period) timeutil.Period {
start := left.Start
if right.Start.After(start) {

View File

@@ -211,7 +211,7 @@ func TestAlertOverlap(t *testing.T) {
}
func TestAlertOverlapUsesOnsetAndEnds(t *testing.T) {
raw := json.RawMessage(`{"event":"Wind Advisory","headline":"Wind Advisory issued June 16 at 12:39PM CDT until June 17 at 8:00PM CDT by NWS St Louis MO","severity":"Moderate","description":"Southwest winds 20 to 30 mph with gusts up to 45 mph expected.","instruction":"Secure outdoor objects.","effective":"2026-06-16T17:39:00Z","onset":"2026-06-17T18:00:00Z","ends":"2026-06-18T01:00:00Z","expires":"2026-06-17T08:45:00Z"}`)
raw := json.RawMessage(`{"event":"Wind Advisory","headline":"Wind Advisory issued June 16 at 12:39PM CDT until June 17 at 8:00PM CDT by NWS St Louis MO","severity":"Moderate","description":"Southwest winds 20 to 30 mph with gusts up to 45 mph expected.","instruction":"Secure outdoor objects.\n\nUse extra\tcaution.","effective":"2026-06-16T17:39:00Z","onset":"2026-06-17T18:00:00Z","ends":"2026-06-18T01:00:00Z","expires":"2026-06-17T08:45:00Z"}`)
alertRun := &weatherdata.AlertRun{Alerts: []json.RawMessage{raw}}
period := timeutil.Period{
Start: mustParse("2026-06-17T12:00:00-05:00"),
@@ -237,11 +237,34 @@ func TestAlertOverlapUsesOnsetAndEnds(t *testing.T) {
if overlaps[0].Description != "Southwest winds 20 to 30 mph with gusts up to 45 mph expected." {
t.Fatalf("Description = %q, want preserved description", overlaps[0].Description)
}
if overlaps[0].Instruction != "Secure outdoor objects." {
if overlaps[0].Instruction != "Secure outdoor objects. Use extra caution." {
t.Fatalf("Instruction = %q, want preserved instruction", overlaps[0].Instruction)
}
}
func TestCollapseWhitespace(t *testing.T) {
tests := []struct {
name string
value string
want string
}{
{name: "newlines", value: "Secure outdoor objects.\nUse extra caution.", want: "Secure outdoor objects. Use extra caution."},
{name: "crlf", value: "Secure outdoor objects.\r\nUse extra caution.", want: "Secure outdoor objects. Use extra caution."},
{name: "tabs and repeated spaces", value: "Secure\toutdoor objects.", want: "Secure outdoor objects."},
{name: "leading trailing", value: " Secure outdoor objects. ", want: "Secure outdoor objects."},
{name: "empty", value: "", want: ""},
{name: "all whitespace", value: " \n\t\r\n ", want: ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := collapseWhitespace(tt.value); got != tt.want {
t.Fatalf("collapseWhitespace(%q) = %q, want %q", tt.value, got, tt.want)
}
})
}
}
func TestAlertOverlapUsesOnsetAndEndsForLocalDateRelevance(t *testing.T) {
location, err := time.LoadLocation("America/Chicago")
if err != nil {