Implement alert instruction whitespace normalization
This commit is contained in:
@@ -492,7 +492,7 @@ func parseAlert(raw json.RawMessage) (parsedAlert, bool) {
|
|||||||
Headline: firstStringField(fields, "headline", "title"),
|
Headline: firstStringField(fields, "headline", "title"),
|
||||||
Severity: stringField(fields, "severity"),
|
Severity: stringField(fields, "severity"),
|
||||||
Description: stringField(fields, "description"),
|
Description: stringField(fields, "description"),
|
||||||
Instruction: stringField(fields, "instruction"),
|
Instruction: collapseWhitespace(stringField(fields, "instruction")),
|
||||||
}
|
}
|
||||||
start, startOK := firstTimeField(fields, "onset", "startsAt", "startTime", "effective", "sent")
|
start, startOK := firstTimeField(fields, "onset", "startsAt", "startTime", "effective", "sent")
|
||||||
end, endOK := firstTimeField(fields, "ends", "endsAt", "endTime", "expires")
|
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
|
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 {
|
func intersect(left timeutil.Period, right timeutil.Period) timeutil.Period {
|
||||||
start := left.Start
|
start := left.Start
|
||||||
if right.Start.After(start) {
|
if right.Start.After(start) {
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ func TestAlertOverlap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAlertOverlapUsesOnsetAndEnds(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}}
|
alertRun := &weatherdata.AlertRun{Alerts: []json.RawMessage{raw}}
|
||||||
period := timeutil.Period{
|
period := timeutil.Period{
|
||||||
Start: mustParse("2026-06-17T12:00:00-05:00"),
|
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." {
|
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)
|
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)
|
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) {
|
func TestAlertOverlapUsesOnsetAndEndsForLocalDateRelevance(t *testing.T) {
|
||||||
location, err := time.LoadLocation("America/Chicago")
|
location, err := time.LoadLocation("America/Chicago")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user