Scan NWS forecast discussion blocks once

This commit is contained in:
2026-08-02 22:56:03 +00:00
parent 2b19a121fa
commit 0a3e52d0e5
2 changed files with 176 additions and 76 deletions

View File

@@ -121,22 +121,30 @@ func ParseForecastDiscussionText(text string) (ForecastDiscussion, error) {
IssuedAt: issuedAt.UTC(),
}
if block, ok := extractForecastDiscussionSection(lines, forecastDiscussionSectionForRole(forecastDiscussionSectionRoleKeyMessages)); ok {
out.KeyMessages = parseForecastDiscussionKeyMessages(block.body)
}
if block, ok := extractForecastDiscussionSection(lines, forecastDiscussionSectionForRole(forecastDiscussionSectionRoleShortTerm)); ok {
section, err := parseForecastDiscussionTextSection(block)
if err != nil {
return ForecastDiscussion{}, fmt.Errorf("parse %s: %w", block.heading.section, err)
seenRoles := make(map[forecastDiscussionSectionRole]bool, len(forecastDiscussionSectionRoles))
for _, block := range parseForecastDiscussionSectionBlocks(lines) {
role, ok := forecastDiscussionSectionRoles[block.heading.section]
if !ok || seenRoles[role] {
continue
}
out.ShortTerm = &section
}
if block, ok := extractForecastDiscussionSection(lines, forecastDiscussionSectionForRole(forecastDiscussionSectionRoleLongTerm)); ok {
section, err := parseForecastDiscussionTextSection(block)
if err != nil {
return ForecastDiscussion{}, fmt.Errorf("parse %s: %w", block.heading.section, err)
seenRoles[role] = true
switch role {
case forecastDiscussionSectionRoleKeyMessages:
out.KeyMessages = parseForecastDiscussionKeyMessages(block.body)
case forecastDiscussionSectionRoleShortTerm:
section, err := parseForecastDiscussionTextSection(block)
if err != nil {
return ForecastDiscussion{}, fmt.Errorf("parse %s: %w", block.heading.section, err)
}
out.ShortTerm = &section
case forecastDiscussionSectionRoleLongTerm:
section, err := parseForecastDiscussionTextSection(block)
if err != nil {
return ForecastDiscussion{}, fmt.Errorf("parse %s: %w", block.heading.section, err)
}
out.LongTerm = &section
}
out.LongTerm = &section
}
return out, nil
@@ -500,36 +508,42 @@ func isForecastDiscussionHorizontalWhitespace(b byte) bool {
return b == ' ' || b == '\t'
}
func forecastDiscussionSectionForRole(role forecastDiscussionSectionRole) string {
for section, registeredRole := range forecastDiscussionSectionRoles {
if registeredRole == role {
return section
}
}
return ""
}
func parseForecastDiscussionSectionBlocks(lines []string) []forecastDiscussionSectionBlock {
var blocks []forecastDiscussionSectionBlock
var active *forecastDiscussionSectionBlock
func extractForecastDiscussionSection(lines []string, section string) (forecastDiscussionSectionBlock, bool) {
for i, raw := range lines {
heading, ok := parseForecastDiscussionSectionHeading(raw)
if !ok || heading.section != section {
finish := func() {
if active == nil {
return
}
blocks = append(blocks, *active)
active = nil
}
for _, raw := range lines {
line := strings.TrimSpace(raw)
if line == "$$" {
finish()
break
}
if line == "&&" || strings.Contains(line, "WATCHES/WARNINGS/ADVISORIES") {
finish()
continue
}
block := forecastDiscussionSectionBlock{heading: heading}
for j := i + 1; j < len(lines); j++ {
next := strings.TrimSpace(lines[j])
if next == "&&" || next == "$$" || strings.Contains(next, "WATCHES/WARNINGS/ADVISORIES") {
break
}
if _, ok := parseForecastDiscussionSectionHeading(next); ok {
break
}
block.body = append(block.body, lines[j])
heading, ok := parseForecastDiscussionSectionHeading(raw)
if ok {
finish()
active = &forecastDiscussionSectionBlock{heading: heading}
continue
}
if active != nil {
active.body = append(active.body, raw)
}
return block, true
}
return forecastDiscussionSectionBlock{}, false
finish()
return blocks
}
func parseForecastDiscussionKeyMessages(body []string) []string {