Update the Alert Digest partial template to be more concise
This commit is contained in:
@@ -81,7 +81,10 @@ Today, Tomorrow, and Hourly call the shared `alert_digest` and
|
|||||||
template at render time and receive the same typed render context as the
|
template at render time and receive the same typed render context as the
|
||||||
caller. The `alert_digest` partial renders the combined Alerts and Risk
|
caller. The `alert_digest` partial renders the combined Alerts and Risk
|
||||||
Products section from relevant NWS alerts and curated SPC outlook digest
|
Products section from relevant NWS alerts and curated SPC outlook digest
|
||||||
records.
|
records. Rendered NWS alert bullets include alert identity and timing but omit
|
||||||
|
instruction and description text. Rendered SPC outlook bullets start at
|
||||||
|
Enhanced Risk; lower-risk SPC entries may still exist in module snapshots and
|
||||||
|
data packages.
|
||||||
|
|
||||||
## Schema Contract
|
## Schema Contract
|
||||||
|
|
||||||
|
|||||||
@@ -761,7 +761,7 @@ func TestGenerateHourlyReportUsesGeneratedTextTemplateWorkflow(t *testing.T) {
|
|||||||
"# Hourly Report",
|
"# Hourly Report",
|
||||||
"Storm chances increase through late morning.",
|
"Storm chances increase through late morning.",
|
||||||
"## Alert Digest",
|
"## Alert Digest",
|
||||||
"- **Flood Watch**: Flood Watch in effect from May 29 at 11:00 AM to May 29 at 3:00 PM. Avoid low-water crossings.",
|
"- **Flood Watch**: Flood Watch in effect from May 29 at 11:00 AM to May 29 at 3:00 PM.",
|
||||||
"## Precipitation Timing",
|
"## Precipitation Timing",
|
||||||
"A cold front is moving into the region.",
|
"A cold front is moving into the region.",
|
||||||
"A front will keep the region unsettled.",
|
"A front will keep the region unsettled.",
|
||||||
@@ -770,6 +770,9 @@ func TestGenerateHourlyReportUsesGeneratedTextTemplateWorkflow(t *testing.T) {
|
|||||||
t.Fatalf("rendered hourly report missing %q:\n%s", want, reportText)
|
t.Fatalf("rendered hourly report missing %q:\n%s", want, reportText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if strings.Contains(reportText, "Avoid low-water crossings.") {
|
||||||
|
t.Fatalf("rendered hourly report includes alert instruction:\n%s", reportText)
|
||||||
|
}
|
||||||
if result.OutputPath != result.ReportPath {
|
if result.OutputPath != result.ReportPath {
|
||||||
t.Fatalf("OutputPath = %q, want managed report path %q", result.OutputPath, result.ReportPath)
|
t.Fatalf("OutputPath = %q, want managed report path %q", result.OutputPath, result.ReportPath)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ func TestBuildHourlyRenderContext(t *testing.T) {
|
|||||||
"**Updated:** Friday, May 29, 2026 at 8:30 AM",
|
"**Updated:** Friday, May 29, 2026 at 8:30 AM",
|
||||||
"Storm chances increase through late morning.",
|
"Storm chances increase through late morning.",
|
||||||
"- **10:00 AM:** 75°F and showers. Probability of precipitation is 70%.",
|
"- **10:00 AM:** 75°F and showers. Probability of precipitation is 70%.",
|
||||||
"- **Flood Watch**: Flood Watch in effect from May 29 at 10:00 AM to May 29 at 2:30 PM. Avoid low-water crossings.",
|
"- **Flood Watch**: Flood Watch in effect from May 29 at 10:00 AM to May 29 at 2:30 PM.",
|
||||||
"A cold front is moving into the region.",
|
"A cold front is moving into the region.",
|
||||||
"A front will keep the region unsettled.",
|
"A front will keep the region unsettled.",
|
||||||
} {
|
} {
|
||||||
@@ -99,6 +99,9 @@ func TestBuildHourlyRenderContext(t *testing.T) {
|
|||||||
t.Fatalf("rendered template missing %q:\n%s", want, text)
|
t.Fatalf("rendered template missing %q:\n%s", want, text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if strings.Contains(text, "Avoid low-water crossings.") {
|
||||||
|
t.Fatalf("rendered template includes alert instruction:\n%s", text)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildHourlyRenderContextAllowsOmittedOptionalModules(t *testing.T) {
|
func TestBuildHourlyRenderContextAllowsOmittedOptionalModules(t *testing.T) {
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ func Render(id string, data any) ([]byte, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tmpl, err := template.New(id).Option("missingkey=error").Parse(source)
|
tmpl, err := template.New(id).Funcs(templateFuncs()).Option("missingkey=error").Parse(source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parse report template %q: %w", id, err)
|
return nil, fmt.Errorf("parse report template %q: %w", id, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,8 +270,7 @@ func TestRenderHourly(t *testing.T) {
|
|||||||
"Storm chances increase through late morning.",
|
"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.",
|
"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.",
|
||||||
"## Alert Digest",
|
"## 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.",
|
"- **Flood Watch**: Flood Watch in effect from May 29 at 10:00 AM to May 29 at 2:30 PM.",
|
||||||
"- **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.",
|
"- **9:00 AM:** 74°F and cloudy.",
|
||||||
"- **10:00 AM:** 75°F and showers. Probability of precipitation is 70%.",
|
"- **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.",
|
"- **10:00 AM** to **12:00 PM**: Expect showers. The peak precipitation chance is 70% at 10:00 AM.",
|
||||||
@@ -285,6 +284,11 @@ func TestRenderHourly(t *testing.T) {
|
|||||||
if strings.Contains(text, "19%") || strings.Contains(text, "wind S") || strings.Contains(text, "## Confidence") {
|
if strings.Contains(text, "19%") || strings.Contains(text, "wind S") || strings.Contains(text, "## Confidence") {
|
||||||
t.Fatalf("rendered template included omitted details:\n%s", text)
|
t.Fatalf("rendered template included omitted details:\n%s", text)
|
||||||
}
|
}
|
||||||
|
for _, unwanted := range []string{"Avoid low-water crossings.", "Slight risk for severe thunderstorms"} {
|
||||||
|
if strings.Contains(text, unwanted) {
|
||||||
|
t.Fatalf("rendered template included %q:\n%s", unwanted, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
assertOrderedText(t, text, []string{
|
assertOrderedText(t, text, []string{
|
||||||
"# Hourly Report",
|
"# Hourly Report",
|
||||||
"## Alert Digest",
|
"## Alert Digest",
|
||||||
@@ -371,8 +375,7 @@ func TestRenderTomorrow(t *testing.T) {
|
|||||||
"**Updated:** Sunday, June 14, 2026 at 9:14 AM",
|
"**Updated:** Sunday, June 14, 2026 at 9:14 AM",
|
||||||
"Tomorrow starts dry before showers return later in the day.",
|
"Tomorrow starts dry before showers return later in the day.",
|
||||||
"## Alert Digest",
|
"## 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.",
|
"- **Wind Advisory**: Wind Advisory in effect from June 15 at 1:00 PM to June 15 at 8:00 PM.",
|
||||||
"- **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.",
|
"- **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.",
|
"- **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%.",
|
"- **Afternoon:** Sunny, with temperatures in the upper 70s. Chance of precipitation is 70%.",
|
||||||
@@ -388,6 +391,8 @@ func TestRenderTomorrow(t *testing.T) {
|
|||||||
for _, unwanted := range []string{
|
for _, unwanted := range []string{
|
||||||
"upper 50s.\n\n- **Morning:**",
|
"upper 50s.\n\n- **Morning:**",
|
||||||
"upper 60s.\n\n- **Afternoon:**",
|
"upper 60s.\n\n- **Afternoon:**",
|
||||||
|
"Secure outdoor objects.",
|
||||||
|
"Slight risk for severe thunderstorms",
|
||||||
} {
|
} {
|
||||||
if strings.Contains(text, unwanted) {
|
if strings.Contains(text, unwanted) {
|
||||||
t.Fatalf("rendered template includes blank lines between daypart bullets:\n%s", text)
|
t.Fatalf("rendered template includes blank lines between daypart bullets:\n%s", text)
|
||||||
@@ -473,7 +478,7 @@ func TestRenderDaily(t *testing.T) {
|
|||||||
"**Updated:** Sunday, June 14, 2026 at 9:14 AM",
|
"**Updated:** Sunday, June 14, 2026 at 9:14 AM",
|
||||||
"The selected day starts dry before showers return later in the day.",
|
"The selected day starts dry before showers return later in the day.",
|
||||||
"## Alert Digest",
|
"## 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.",
|
"- **Flood Watch**: Flood Watch in effect from June 15 at 3:00 PM to June 15 at 6:00 PM.",
|
||||||
"- **SPC Convective Outlook**: Enhanced risk for severe thunderstorms in effect from June 15 at 7:00 AM to June 16 at 7:00 AM.",
|
"- **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.",
|
"- **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%.",
|
"- **Afternoon:** Showers, with temperatures in the upper 70s. Chance of precipitation is 70%.",
|
||||||
@@ -486,6 +491,9 @@ func TestRenderDaily(t *testing.T) {
|
|||||||
t.Fatalf("rendered template missing %q:\n%s", want, text)
|
t.Fatalf("rendered template missing %q:\n%s", want, text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if strings.Contains(text, "Monitor creek levels.") {
|
||||||
|
t.Fatalf("rendered template included alert description:\n%s", text)
|
||||||
|
}
|
||||||
assertOrderedText(t, text, []string{
|
assertOrderedText(t, text, []string{
|
||||||
"# Monday's Weather",
|
"# Monday's Weather",
|
||||||
"The selected day starts dry before showers return later in the day.",
|
"The selected day starts dry before showers return later in the day.",
|
||||||
@@ -581,8 +589,7 @@ func TestRenderToday(t *testing.T) {
|
|||||||
"**Updated:** Monday, June 15, 2026 at 7:14 AM",
|
"**Updated:** Monday, June 15, 2026 at 7:14 AM",
|
||||||
"Today starts dry before showers return later in the day.",
|
"Today starts dry before showers return later in the day.",
|
||||||
"## Alert Digest",
|
"## 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.",
|
"- **Wind Advisory**: Wind Advisory in effect from June 15 at 1:00 PM to June 15 at 8:00 PM.",
|
||||||
"- **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.",
|
"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.",
|
"- **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%.",
|
"- **Afternoon:** Showers, with temperatures in the upper 70s. Chance of precipitation is 70%.",
|
||||||
@@ -606,7 +613,7 @@ func TestRenderToday(t *testing.T) {
|
|||||||
"## Precipitation Timing",
|
"## Precipitation Timing",
|
||||||
"## Forecast Discussion",
|
"## Forecast Discussion",
|
||||||
})
|
})
|
||||||
for _, unwanted := range []string{"## Planning Notes", "Morning weather looks routine.", "Watch late-day shower timing.", "- **Evening:**", "Forecast details are limited"} {
|
for _, unwanted := range []string{"## Planning Notes", "Morning weather looks routine.", "Watch late-day shower timing.", "- **Evening:**", "Forecast details are limited", "Secure outdoor objects.", "Slight risk for severe thunderstorms"} {
|
||||||
if strings.Contains(text, unwanted) {
|
if strings.Contains(text, unwanted) {
|
||||||
t.Fatalf("rendered template included %q:\n%s", unwanted, text)
|
t.Fatalf("rendered template included %q:\n%s", unwanted, text)
|
||||||
}
|
}
|
||||||
@@ -736,6 +743,73 @@ func TestRenderTomorrowOmitsPrecipitationTimingWithoutWindows(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAlertDigestOmitsBelowThresholdSPCRiskOnlySection(t *testing.T) {
|
||||||
|
rendered, err := Render("hourly", testRenderContext{
|
||||||
|
Report: testReportContext{Title: "Hourly Report"},
|
||||||
|
GeneratedText: testGeneratedText{
|
||||||
|
Summary: "Storm chances remain low.",
|
||||||
|
ForecastDiscussion: "Only isolated severe storms are possible.",
|
||||||
|
},
|
||||||
|
Modules: testModules{
|
||||||
|
CurrentConditions: &testCurrentConditions{},
|
||||||
|
HourlyForecast: &testHourlyForecast{},
|
||||||
|
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 {
|
||||||
|
t.Fatalf("Render() error = %v", err)
|
||||||
|
}
|
||||||
|
text := string(rendered)
|
||||||
|
for _, unwanted := range []string{"## Alert Digest", "SPC Convective Outlook", "Slight risk for severe thunderstorms"} {
|
||||||
|
if strings.Contains(text, unwanted) {
|
||||||
|
t.Fatalf("rendered template included %q for below-threshold SPC-only digest:\n%s", unwanted, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAlertDigestRendersEnhancedOrHigherSPCRiskWithoutAlerts(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
label string
|
||||||
|
risk string
|
||||||
|
}{
|
||||||
|
{label: "Enhanced Risk", risk: "Enhanced risk"},
|
||||||
|
{label: "Moderate Risk", risk: "Moderate risk"},
|
||||||
|
{label: "High Risk", risk: "High risk"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.label, func(t *testing.T) {
|
||||||
|
rendered, err := Render("hourly", testRenderContext{
|
||||||
|
Report: testReportContext{Title: "Hourly Report"},
|
||||||
|
GeneratedText: testGeneratedText{
|
||||||
|
Summary: "Severe storms are possible.",
|
||||||
|
ForecastDiscussion: "SPC outlooks highlight the risk.",
|
||||||
|
},
|
||||||
|
Modules: testModules{
|
||||||
|
CurrentConditions: &testCurrentConditions{},
|
||||||
|
HourlyForecast: &testHourlyForecast{},
|
||||||
|
SPCConvectiveOutlooks: &testSPCOutlooks{
|
||||||
|
RiskDigest: []testSPCRiskDigest{{LabelText: tt.label, RiskLabel: tt.risk, PeriodBegins: "June 15 at 7:00 AM", PeriodEnds: "June 16 at 7:00 AM"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Render() error = %v", err)
|
||||||
|
}
|
||||||
|
text := string(rendered)
|
||||||
|
for _, want := range []string{
|
||||||
|
"## Alert Digest",
|
||||||
|
"- **SPC Convective Outlook**: " + tt.risk + " for severe thunderstorms in effect from June 15 at 7:00 AM to June 16 at 7:00 AM.",
|
||||||
|
} {
|
||||||
|
if !strings.Contains(text, want) {
|
||||||
|
t.Fatalf("rendered template missing %q:\n%s", want, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRenderHourlyOmitsConditionalSectionsForClearWeather(t *testing.T) {
|
func TestRenderHourlyOmitsConditionalSectionsForClearWeather(t *testing.T) {
|
||||||
rendered, err := Render("hourly", testRenderContext{
|
rendered, err := Render("hourly", testRenderContext{
|
||||||
Report: testReportContext{
|
Report: testReportContext{
|
||||||
|
|||||||
76
internal/reporttemplate/template_funcs.go
Normal file
76
internal/reporttemplate/template_funcs.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package reporttemplate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
func templateFuncs() template.FuncMap {
|
||||||
|
return template.FuncMap{
|
||||||
|
"hasRelevantAlerts": hasRelevantAlerts,
|
||||||
|
"hasEnhancedOrHigherSPCRisk": hasEnhancedOrHigherSPCRisk,
|
||||||
|
"isEnhancedOrHigherSPCRisk": isEnhancedOrHigherSPCRisk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasRelevantAlerts(alertDigest any) bool {
|
||||||
|
value := dereferenceValue(reflect.ValueOf(alertDigest))
|
||||||
|
if !value.IsValid() || value.Kind() != reflect.Struct {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
relevant := value.FieldByName("Relevant")
|
||||||
|
return relevant.IsValid() && relevant.Kind() == reflect.Slice && relevant.Len() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasEnhancedOrHigherSPCRisk(outlooks any) bool {
|
||||||
|
value := dereferenceValue(reflect.ValueOf(outlooks))
|
||||||
|
if !value.IsValid() || value.Kind() != reflect.Struct {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
riskDigest := value.FieldByName("RiskDigest")
|
||||||
|
if !riskDigest.IsValid() || riskDigest.Kind() != reflect.Slice {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < riskDigest.Len(); i++ {
|
||||||
|
if isEnhancedOrHigherSPCRisk(riskDigest.Index(i).Interface()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEnhancedOrHigherSPCRisk(risk any) bool {
|
||||||
|
value := dereferenceValue(reflect.ValueOf(risk))
|
||||||
|
if !value.IsValid() || value.Kind() != reflect.Struct {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
label := stringField(value, "LabelText")
|
||||||
|
if label == "" {
|
||||||
|
label = stringField(value, "RiskLabel")
|
||||||
|
}
|
||||||
|
switch strings.ToLower(strings.TrimSpace(label)) {
|
||||||
|
case "enhanced risk", "moderate risk", "high risk":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dereferenceValue(value reflect.Value) reflect.Value {
|
||||||
|
for value.IsValid() && (value.Kind() == reflect.Interface || value.Kind() == reflect.Pointer) {
|
||||||
|
if value.IsNil() {
|
||||||
|
return reflect.Value{}
|
||||||
|
}
|
||||||
|
value = value.Elem()
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringField(value reflect.Value, name string) string {
|
||||||
|
field := value.FieldByName(name)
|
||||||
|
if !field.IsValid() || field.Kind() != reflect.String {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return field.String()
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
{{ define "alert_digest" }}{{ if or (and .Modules.AlertDigest .Modules.AlertDigest.Relevant) (and .Modules.SPCConvectiveOutlooks .Modules.SPCConvectiveOutlooks.RiskDigest) }}## Alert Digest
|
{{ define "alert_digest" }}{{ $hasAlerts := hasRelevantAlerts .Modules.AlertDigest }}{{ $hasSPC := hasEnhancedOrHigherSPCRisk .Modules.SPCConvectiveOutlooks }}{{ if or $hasAlerts $hasSPC }}## Alert Digest
|
||||||
{{ with .Modules.AlertDigest }}{{ range .Relevant -}}
|
{{ 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 }}
|
- **{{ if .Event }}{{ .Event }}{{ else }}{{ .Headline }}{{ end }}**: {{ if .Event }}{{ .Event }}{{ else }}{{ .Headline }}{{ end }} in effect{{ with .PeriodBegins }} from {{ . }}{{ end }}{{ with .PeriodEnds }} to {{ . }}{{ end }}.
|
||||||
{{ end }}{{ end }}{{ with .Modules.SPCConvectiveOutlooks }}{{ range .RiskDigest -}}
|
{{ end }}{{ end }}{{ with .Modules.SPCConvectiveOutlooks }}{{ range .RiskDigest }}{{ if isEnhancedOrHigherSPCRisk . -}}
|
||||||
- **SPC Convective Outlook**: {{ with .RiskLabel }}{{ . }}{{ else }}Convective risk{{ end }} for severe thunderstorms in effect{{ with .PeriodBegins }} from {{ . }}{{ end }}{{ with .PeriodEnds }} to {{ . }}{{ end }}.
|
- **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 }}{{ end }}
|
||||||
|
|
||||||
{{ end }}{{ end }}
|
{{ end }}{{ end }}
|
||||||
|
|||||||
Reference in New Issue
Block a user