Handle additional NWS forecast discussion formats
All checks were successful
ci/woodpecker/manual/build-image Pipeline was successful
All checks were successful
ci/woodpecker/manual/build-image Pipeline was successful
This commit is contained in:
@@ -391,6 +391,58 @@ func TestParseForecastDiscussionSectionBlocksUsesGenericHeadingsAsBoundaries(t *
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseForecastDiscussionSectionBlocksRecognizesEmbeddedHeadingsInPreviousDiscussion(t *testing.T) {
|
||||
got := parseForecastDiscussionSectionBlocks([]string{
|
||||
".PREV DISCUSSION... /Issued 319 PM PDT Sun Aug 2 2026/",
|
||||
"KEY MESSAGES...",
|
||||
"* First embedded message.",
|
||||
"DISCUSSION...",
|
||||
"Discussion prose.",
|
||||
"&&",
|
||||
})
|
||||
want := []forecastDiscussionSectionBlock{
|
||||
{
|
||||
heading: forecastDiscussionSectionHeading{
|
||||
section: "PREV DISCUSSION",
|
||||
qualifier: "/Issued 319 PM PDT Sun Aug 2 2026/",
|
||||
},
|
||||
},
|
||||
{
|
||||
heading: forecastDiscussionSectionHeading{section: "KEY MESSAGES"},
|
||||
body: []string{"* First embedded message."},
|
||||
},
|
||||
{
|
||||
heading: forecastDiscussionSectionHeading{section: "DISCUSSION"},
|
||||
body: []string{"Discussion prose."},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("blocks = %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseForecastDiscussionSectionBlocksKeepsUndottedHeadingLikeLinesOutsideWrappers(t *testing.T) {
|
||||
got := parseForecastDiscussionSectionBlocks([]string{
|
||||
".SHORT TERM...",
|
||||
"Short-term prose.",
|
||||
"KEY MESSAGES...",
|
||||
"Still short-term prose.",
|
||||
})
|
||||
want := []forecastDiscussionSectionBlock{
|
||||
{
|
||||
heading: forecastDiscussionSectionHeading{section: "SHORT TERM"},
|
||||
body: []string{
|
||||
"Short-term prose.",
|
||||
"KEY MESSAGES...",
|
||||
"Still short-term prose.",
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("blocks = %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseForecastDiscussionSectionBlocksUsesParenthesizedTerminalHeadingAsBoundary(t *testing.T) {
|
||||
got := parseForecastDiscussionSectionBlocks([]string{
|
||||
".SHORT TERM... (Tonight)",
|
||||
@@ -756,6 +808,16 @@ func TestIsForecastDiscussionKeyMessageMetadataLine(t *testing.T) {
|
||||
line: "uPdAtEd At 300 PM CDT Sat Mar 28 2026",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "as of weekday preamble",
|
||||
line: "As of 1235 PM Sunday...",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "as of ASCII case insensitive",
|
||||
line: "aS oF 1235 pm SuNdAy...",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "label without whitespace boundary",
|
||||
line: "Updated at300 PM CDT Sat Mar 28 2026",
|
||||
@@ -766,6 +828,26 @@ func TestIsForecastDiscussionKeyMessageMetadataLine(t *testing.T) {
|
||||
line: "Updated at not a timestamp",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "as of invalid clock",
|
||||
line: "As of 2535 PM Sunday...",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "as of invalid weekday",
|
||||
line: "As of 1235 PM Someday...",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "as of without terminal ellipsis",
|
||||
line: "As of 1235 PM Sunday",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "as of prefix collision",
|
||||
line: "As often happens, storms weaken overnight.",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "nonmetadata prose",
|
||||
line: "Updated atmospheric conditions remain unsettled.",
|
||||
@@ -794,13 +876,18 @@ func TestStripForecastDiscussionKeyMessageMarker(t *testing.T) {
|
||||
{name: "asterisk", line: "* First message.", want: "First message.", ok: true},
|
||||
{name: "numeric parenthesis", line: "1) First message.", want: "First message.", ok: true},
|
||||
{name: "numeric dot", line: "2. Second message.", want: "Second message.", ok: true},
|
||||
{name: "parenthesized numeric", line: "(1) First message.", want: "First message.", ok: true},
|
||||
{name: "composite hyphen", line: "- 1. First message.", want: "First message.", ok: true},
|
||||
{name: "composite asterisk", line: "* 1) First message.", want: "First message.", ok: true},
|
||||
{name: "composite parenthesized numeric", line: "- (1) First message.", want: "First message.", ok: true},
|
||||
{name: "composite asterisk parenthesized numeric", line: "* (1) First message.", want: "First message.", ok: true},
|
||||
{name: "multi digit numeric", line: "12. Twelfth message.", want: "Twelfth message.", ok: true},
|
||||
{name: "hyphen only", line: "-", want: "", ok: true},
|
||||
{name: "numeric marker only", line: "1)", want: "", ok: true},
|
||||
{name: "zero numeric marker", line: "0) Not a marker.", want: "", ok: false},
|
||||
{name: "zero parenthesized numeric marker", line: "(0) Not a marker.", want: "", ok: false},
|
||||
{name: "overflow numeric marker", line: "999999999999999999999999999999. Too big.", want: "", ok: false},
|
||||
{name: "parenthesized numeric marker without boundary", line: "(1)Not a marker.", want: "", ok: false},
|
||||
{name: "alphanumeric numeric prefix", line: "1x Not a marker.", want: "", ok: false},
|
||||
{name: "numeric marker without boundary", line: "1)Not a marker.", want: "", ok: false},
|
||||
{name: "numeric marker with unsupported punctuation", line: "1, Not a marker.", want: "", ok: false},
|
||||
@@ -877,6 +964,14 @@ func TestParseForecastDiscussionKeyMessagesClassifiesListsAndMetadata(t *testing
|
||||
},
|
||||
want: []string{"First message."},
|
||||
},
|
||||
{
|
||||
name: "leading as of metadata",
|
||||
body: []string{
|
||||
"As of 1235 PM Sunday...",
|
||||
"- First message.",
|
||||
},
|
||||
want: []string{"First message."},
|
||||
},
|
||||
{
|
||||
name: "updated atmospheric prose remains content",
|
||||
body: []string{
|
||||
@@ -901,6 +996,14 @@ func TestParseForecastDiscussionKeyMessagesClassifiesListsAndMetadata(t *testing
|
||||
},
|
||||
want: []string{"First message. Updated at 300 PM CDT Sat Mar 28 2026"},
|
||||
},
|
||||
{
|
||||
name: "later as of metadata remains content",
|
||||
body: []string{
|
||||
"- First message.",
|
||||
"As of 1235 PM Sunday...",
|
||||
},
|
||||
want: []string{"First message. As of 1235 PM Sunday..."},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -1089,7 +1192,7 @@ func TestParseForecastDiscussionHTMLSupportsMixedHeadingFormats(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseForecastDiscussionHTMLParsesNumberedAndKeyPointsFixtures(t *testing.T) {
|
||||
func TestParseForecastDiscussionHTMLParsesCrossOfficeKeyMessageFixtures(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
filename string
|
||||
@@ -1127,6 +1230,48 @@ func TestParseForecastDiscussionHTMLParsesNumberedAndKeyPointsFixtures(t *testin
|
||||
"*", "DISCUSSION", "(Today through Thursday)", "Discussion details must not be included with key points.",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "as of preamble",
|
||||
filename: "forecast_discussion_rah_as_of_sample.html",
|
||||
officeID: "RAH",
|
||||
officeName: "National Weather Service Raleigh NC",
|
||||
issuedAt: time.Date(2026, 8, 2, 16, 35, 0, 0, time.UTC),
|
||||
messages: []string{
|
||||
"Scattered storms may produce locally heavy rain this afternoon.",
|
||||
"Drier weather arrives Monday.",
|
||||
},
|
||||
unwanted: []string{
|
||||
"As of", "1)", "2)", "DISCUSSION", "Discussion details remain boundary-only content.",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "parenthesized numeric markers",
|
||||
filename: "forecast_discussion_lwx_parenthesized_number_sample.html",
|
||||
officeID: "LWX",
|
||||
officeName: "National Weather Service Baltimore MD/Washington DC",
|
||||
issuedAt: time.Date(2026, 8, 2, 18, 0, 0, 0, time.UTC),
|
||||
messages: []string{
|
||||
"Thunderstorms remain possible near the Blue Ridge this evening.",
|
||||
"Seasonably warm conditions continue Monday.",
|
||||
},
|
||||
unwanted: []string{
|
||||
"(1)", "(2)", "AVIATION", "Aviation details remain boundary-only content.",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "embedded key messages in previous discussion",
|
||||
filename: "forecast_discussion_mfr_prev_discussion_sample.html",
|
||||
officeID: "MFR",
|
||||
officeName: "National Weather Service Medford OR",
|
||||
issuedAt: time.Date(2026, 8, 2, 22, 19, 0, 0, time.UTC),
|
||||
messages: []string{
|
||||
"Heat returns to inland valleys Monday.",
|
||||
"Gusty afternoon winds develop east of the Cascades.",
|
||||
},
|
||||
unwanted: []string{
|
||||
"PREV DISCUSSION", "Issued 319 PM", "*", "DISCUSSION", "Discussion details must not be included with key messages.",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
Reference in New Issue
Block a user