Add SPC Outlook summaries to the alert digest template

This commit is contained in:
2026-06-16 21:42:20 -05:00
parent 5d416cfc4a
commit 3900b3313b
10 changed files with 229 additions and 35 deletions

View File

@@ -243,7 +243,8 @@ func TestRenderHourly(t *testing.T) {
Relevant: []testAlert{{Event: "Flood Watch", Headline: "Flood Watch until 2:30 PM", Severity: "Moderate", PeriodBegins: "May 29 at 10:00 AM", PeriodEnds: "May 29 at 2:30 PM", Instruction: "Avoid low-water crossings."}},
},
SPCConvectiveOutlooks: &testSPCOutlooks{
Outlooks: []testSPCOutlook{{LabelText: "Slight Risk", PeriodBegins: "8 AM", PeriodEnds: "2 PM"}},
Outlooks: []testSPCOutlook{{LabelText: "Slight Risk", PeriodBegins: "8 AM", PeriodEnds: "2 PM"}},
RiskDigest: []testSPCRiskDigest{{LabelText: "Slight Risk", RiskLabel: "Slight risk", PeriodBegins: "May 29 at 8:00 AM", PeriodEnds: "May 29 at 2:00 PM"}},
},
SPCConvectiveDiscussion: &testSPCDiscussion{
Discussions: []testSPCDiscussionRecord{{Summary: "Strong storms may develop late morning."}},
@@ -268,8 +269,9 @@ func TestRenderHourly(t *testing.T) {
"**Updated:** Saturday, June 14, 2026 at 9:14 AM",
"Storm chances increase through late morning.",
"Currently, it is 74°F and partly cloudy. It feels like 76°F, with a relative humidity of 71% and winds from the south at 8 mph.",
"## Active Alerts",
"## Alert Digest",
"- **Flood Watch**: Flood Watch in effect from May 29 at 10:00 AM to May 29 at 2:30 PM. Avoid low-water crossings.",
"- **SPC Convective Outlook**: Slight risk for severe thunderstorms in effect from May 29 at 8:00 AM to May 29 at 2:00 PM.",
"- **9:00 AM:** 74°F and cloudy.",
"- **10:00 AM:** 75°F and showers. Probability of precipitation is 70%.",
"- **10:00 AM** to **12:00 PM**: Expect showers. The peak precipitation chance is 70% at 10:00 AM.",
@@ -285,7 +287,7 @@ func TestRenderHourly(t *testing.T) {
}
assertOrderedText(t, text, []string{
"# Hourly Report",
"## Active Alerts",
"## Alert Digest",
"## Current Conditions",
"## Hourly Forecast",
"## Precipitation Timing",
@@ -354,6 +356,9 @@ func TestRenderTomorrow(t *testing.T) {
AlertDigest: &testAlertDigest{
Relevant: []testAlert{{Event: "Wind Advisory", Headline: "Wind Advisory until 8:00 PM", Severity: "Moderate", PeriodBegins: "June 15 at 1:00 PM", PeriodEnds: "June 15 at 8:00 PM", Instruction: "Secure outdoor objects."}},
},
SPCConvectiveOutlooks: &testSPCOutlooks{
RiskDigest: []testSPCRiskDigest{{LabelText: "Slight Risk", RiskLabel: "Slight risk", PeriodBegins: "June 15 at 7:00 AM", PeriodEnds: "June 16 at 7:00 AM"}},
},
},
})
if err != nil {
@@ -365,8 +370,9 @@ func TestRenderTomorrow(t *testing.T) {
"**Forecast date:** Monday, June 15, 2026",
"**Updated:** Sunday, June 14, 2026 at 9:14 AM",
"Tomorrow starts dry before showers return later in the day.",
"## Active Alerts",
"## Alert Digest",
"- **Wind Advisory**: Wind Advisory in effect from June 15 at 1:00 PM to June 15 at 8:00 PM. Secure outdoor objects.",
"- **SPC Convective Outlook**: Slight risk for severe thunderstorms in effect from June 15 at 7:00 AM to June 16 at 7:00 AM.",
"- **Overnight:** Partly cloudy, with temperatures falling from the mid 60s to the upper 50s.",
"- **Morning:** Sunny, with temperatures rising from the upper 50s to the upper 60s.",
"- **Afternoon:** Sunny, with temperatures in the upper 70s. Chance of precipitation is 70%.",
@@ -390,7 +396,7 @@ func TestRenderTomorrow(t *testing.T) {
assertOrderedText(t, text, []string{
"# Monday's Weather",
"Tomorrow starts dry before showers return later in the day.",
"## Active Alerts",
"## Alert Digest",
"## Daypart Forecast",
"- **Overnight:**",
"- **Morning:**",
@@ -452,6 +458,9 @@ func TestRenderDaily(t *testing.T) {
AlertDigest: &testAlertDigest{
Relevant: []testAlert{{Event: "Flood Watch", Headline: "Flood Watch until 6:00 PM", Severity: "Moderate", PeriodBegins: "June 15 at 3:00 PM", PeriodEnds: "June 15 at 6:00 PM", Description: "Monitor creek levels."}},
},
SPCConvectiveOutlooks: &testSPCOutlooks{
RiskDigest: []testSPCRiskDigest{{LabelText: "Enhanced Risk", RiskLabel: "Enhanced risk", PeriodBegins: "June 15 at 7:00 AM", PeriodEnds: "June 16 at 7:00 AM"}},
},
},
})
if err != nil {
@@ -463,8 +472,9 @@ func TestRenderDaily(t *testing.T) {
"**Forecast date:** Monday, June 15, 2026",
"**Updated:** Sunday, June 14, 2026 at 9:14 AM",
"The selected day starts dry before showers return later in the day.",
"## Active Alerts",
"## Alert Digest",
"- **Flood Watch**: Flood Watch in effect from June 15 at 3:00 PM to June 15 at 6:00 PM. Monitor creek levels.",
"- **SPC Convective Outlook**: Enhanced risk for severe thunderstorms in effect from June 15 at 7:00 AM to June 16 at 7:00 AM.",
"- **Morning:** Sunny, with temperatures rising from the upper 50s to the upper 60s.",
"- **Afternoon:** Showers, with temperatures in the upper 70s. Chance of precipitation is 70%.",
"- **3:00 PM** to **6:00 PM**: Expect showers. The peak precipitation chance is 70% at 3:00 PM.",
@@ -479,7 +489,7 @@ func TestRenderDaily(t *testing.T) {
assertOrderedText(t, text, []string{
"# Monday's Weather",
"The selected day starts dry before showers return later in the day.",
"## Active Alerts",
"## Alert Digest",
"## Daypart Forecast",
"- **Morning:**",
"- **Afternoon:**",
@@ -552,6 +562,9 @@ func TestRenderToday(t *testing.T) {
AlertDigest: &testAlertDigest{
Relevant: []testAlert{{Event: "Wind Advisory", Headline: "Wind Advisory until 8:00 PM", Severity: "Moderate", PeriodBegins: "June 15 at 1:00 PM", PeriodEnds: "June 15 at 8:00 PM", Instruction: "Secure outdoor objects."}},
},
SPCConvectiveOutlooks: &testSPCOutlooks{
RiskDigest: []testSPCRiskDigest{{LabelText: "Slight Risk", RiskLabel: "Slight risk", PeriodBegins: "June 15 at 7:00 AM", PeriodEnds: "June 16 at 7:00 AM"}},
},
TodayPlanning: &testTodayPlanning{
MorningReadiness: []string{"Morning weather looks routine."},
LateDayChangeWatch: []string{"Watch late-day shower timing."},
@@ -567,8 +580,9 @@ func TestRenderToday(t *testing.T) {
"**Forecast date:** Monday, June 15, 2026",
"**Updated:** Monday, June 15, 2026 at 7:14 AM",
"Today starts dry before showers return later in the day.",
"## Active Alerts",
"## Alert Digest",
"- **Wind Advisory**: Wind Advisory in effect from June 15 at 1:00 PM to June 15 at 8:00 PM. Secure outdoor objects.",
"- **SPC Convective Outlook**: Slight risk for severe thunderstorms in effect from June 15 at 7:00 AM to June 16 at 7:00 AM.",
"Currently, it is 58°F and clear. It feels like 57°F, with a relative humidity of 61% and winds from the northwest at 9 mph.",
"- **Morning:** Sunny, with temperatures rising from the upper 50s to the upper 60s.",
"- **Afternoon:** Showers, with temperatures in the upper 70s. Chance of precipitation is 70%.",
@@ -584,7 +598,7 @@ func TestRenderToday(t *testing.T) {
assertOrderedText(t, text, []string{
"# Today's Weather",
"Today starts dry before showers return later in the day.",
"## Active Alerts",
"## Alert Digest",
"## Current Conditions",
"## Daypart Forecast",
"- **Morning:**",
@@ -673,8 +687,8 @@ func TestRenderDaypartTemplatesUseRichHelperFields(t *testing.T) {
t.Fatalf("rendered template missing %q:\n%s", want, text)
}
}
if strings.Contains(text, "## Active Alerts") {
t.Fatalf("rendered template includes empty Active Alerts section:\n%s", text)
if strings.Contains(text, "## Alert Digest") {
t.Fatalf("rendered template includes empty Alerts and Risk Products section:\n%s", text)
}
if strings.Contains(text, "\n\n\n") {
t.Fatalf("rendered template includes excess blank lines:\n%s", text)
@@ -752,7 +766,7 @@ func TestRenderHourlyOmitsConditionalSectionsForClearWeather(t *testing.T) {
t.Fatalf("Render() error = %v", err)
}
text := string(rendered)
for _, unwanted := range []string{"## Active Alerts", "## Precipitation Timing", "Probability of precipitation is 10%", "wind"} {
for _, unwanted := range []string{"## Alert Digest", "## Precipitation Timing", "Probability of precipitation is 10%", "wind"} {
if strings.Contains(text, unwanted) {
t.Fatalf("clear render includes %q:\n%s", unwanted, text)
}
@@ -928,23 +942,26 @@ type testModules struct {
}
type testTomorrowModules struct {
Dayparts []testTomorrowDaypart
PrecipTiming *testPrecipTiming
AlertDigest *testAlertDigest
Dayparts []testTomorrowDaypart
PrecipTiming *testPrecipTiming
AlertDigest *testAlertDigest
SPCConvectiveOutlooks *testSPCOutlooks
}
type testTodayModules struct {
CurrentConditions *testCurrentConditions
Dayparts []testTomorrowDaypart
PrecipTiming *testPrecipTiming
AlertDigest *testAlertDigest
TodayPlanning *testTodayPlanning
CurrentConditions *testCurrentConditions
Dayparts []testTomorrowDaypart
PrecipTiming *testPrecipTiming
AlertDigest *testAlertDigest
SPCConvectiveOutlooks *testSPCOutlooks
TodayPlanning *testTodayPlanning
}
type testDailyModules struct {
Dayparts []testDailyDaypart
PrecipTiming *testPrecipTiming
AlertDigest *testAlertDigest
Dayparts []testDailyDaypart
PrecipTiming *testPrecipTiming
AlertDigest *testAlertDigest
SPCConvectiveOutlooks *testSPCOutlooks
}
type testTodayPlanning struct {
@@ -1046,7 +1063,8 @@ type testAlert struct {
}
type testSPCOutlooks struct {
Outlooks []testSPCOutlook
Outlooks []testSPCOutlook
RiskDigest []testSPCRiskDigest
}
type testSPCOutlook struct {
@@ -1057,6 +1075,13 @@ type testSPCOutlook struct {
PeriodEnds string
}
type testSPCRiskDigest struct {
LabelText string
RiskLabel string
PeriodBegins string
PeriodEnds string
}
type testForecastDiscussion struct {
KeyMessages []string
ShortTerm string

View File

@@ -1,6 +1,8 @@
{{ define "alert_digest" }}{{ if and .Modules.AlertDigest .Modules.AlertDigest.Relevant }}## Active Alerts
{{ range .Modules.AlertDigest.Relevant -}}
{{ define "alert_digest" }}{{ if or (and .Modules.AlertDigest .Modules.AlertDigest.Relevant) (and .Modules.SPCConvectiveOutlooks .Modules.SPCConvectiveOutlooks.RiskDigest) }}## Alert Digest
{{ with .Modules.AlertDigest }}{{ range .Relevant -}}
- **{{ if .Event }}{{ .Event }}{{ else }}{{ .Headline }}{{ end }}**: {{ if .Event }}{{ .Event }}{{ else }}{{ .Headline }}{{ end }} in effect{{ with .PeriodBegins }} from {{ . }}{{ end }}{{ with .PeriodEnds }} to {{ . }}{{ end }}.{{ with .Instruction }} {{ . }}{{ else }}{{ with .Description }} {{ . }}{{ end }}{{ end }}
{{ end }}
{{ end }}{{ end }}{{ with .Modules.SPCConvectiveOutlooks }}{{ range .RiskDigest -}}
- **SPC Convective Outlook**: {{ with .RiskLabel }}{{ . }}{{ else }}Convective risk{{ end }} for severe thunderstorms in effect{{ with .PeriodBegins }} from {{ . }}{{ end }}{{ with .PeriodEnds }} to {{ . }}{{ end }}.
{{ end }}{{ end }}
{{ end }}{{ end }}