Updated precipitation timing language in the shared template
This commit is contained in:
@@ -293,6 +293,8 @@ Common fields:
|
||||
| `.Modules.PrecipTiming.PrecipitationWindows[].MaxPopPercent` | *int | Highest precipitation probability inside the window. |
|
||||
| `.Modules.PrecipTiming.PrecipitationWindows[].MaxPopTime` | string | Friendly local time for the window maximum. |
|
||||
| `.Modules.PrecipTiming.PrecipitationWindows[].MaxPopHourLabel` | string | Friendly hour label for the window maximum. |
|
||||
| `.Modules.PrecipTiming.PrecipitationWindows[].PrecipitationType` | string | Conservatively inferred precipitation type, such as `showers and thunderstorms`. |
|
||||
| `.Modules.PrecipTiming.PrecipitationWindows[].ExpectationPhrase` | string | Probability-based sentence used by day-style templates. |
|
||||
| `.Modules.PrecipTiming.ThunderMentioned` | bool | Whether thunder is mentioned in the forecast text. |
|
||||
|
||||
### Daypart Summaries
|
||||
|
||||
@@ -105,9 +105,15 @@ func TestPrecipTimingModuleHandlesRainyAndDryForecasts(t *testing.T) {
|
||||
if rainy.PrecipitationWindows[0].PeriodBegins != "2026-05-29 at 8:00 AM" || rainy.PrecipitationWindows[0].PeriodBeginsHourLabel != "8:00 AM" || rainy.PrecipitationWindows[0].PeriodEnds != "2026-05-29 at 9:00 AM" || rainy.PrecipitationWindows[0].PeriodEndsHourLabel != "9:00 AM" || rainy.PrecipitationWindows[0].MaxPopPercent == nil || *rainy.PrecipitationWindows[0].MaxPopPercent != 60 || rainy.PrecipitationWindows[0].MaxPopHourLabel != "8:00 AM" {
|
||||
t.Fatalf("first precipitation window = %#v, want 8-9 AM at 60%%", rainy.PrecipitationWindows[0])
|
||||
}
|
||||
if rainy.PrecipitationWindows[0].PrecipitationType != "showers" || rainy.PrecipitationWindows[0].ExpectationPhrase != "Showers likely." {
|
||||
t.Fatalf("first precipitation window phrase = %#v, want showers likely", rainy.PrecipitationWindows[0])
|
||||
}
|
||||
if rainy.PrecipitationWindows[1].PeriodBegins != "2026-05-29 at 12:00 PM" || rainy.PrecipitationWindows[1].PeriodBeginsHourLabel != "12:00 PM" || rainy.PrecipitationWindows[1].PeriodEnds != "2026-05-29 at 2:00 PM" || rainy.PrecipitationWindows[1].PeriodEndsHourLabel != "2:00 PM" || rainy.PrecipitationWindows[1].MaxPopPercent == nil || *rainy.PrecipitationWindows[1].MaxPopPercent != 80 || rainy.PrecipitationWindows[1].MaxPopHourLabel != "12:00 PM" {
|
||||
t.Fatalf("second precipitation window = %#v, want noon-2 PM at 80%%", rainy.PrecipitationWindows[1])
|
||||
}
|
||||
if rainy.PrecipitationWindows[1].PrecipitationType != "showers and thunderstorms" || rainy.PrecipitationWindows[1].ExpectationPhrase != "Expect showers and thunderstorms." {
|
||||
t.Fatalf("second precipitation window phrase = %#v, want expect showers and thunderstorms", rainy.PrecipitationWindows[1])
|
||||
}
|
||||
data, err := json.Marshal(output.Value)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal precip timing: %v", err)
|
||||
@@ -115,6 +121,9 @@ func TestPrecipTimingModuleHandlesRainyAndDryForecasts(t *testing.T) {
|
||||
if !strings.Contains(string(data), "precipitation_windows") || !strings.Contains(string(data), "probability_threshold") || !strings.Contains(string(data), "period_begins_hour_label") || !strings.Contains(string(data), "max_pop_hour_label") {
|
||||
t.Fatalf("precip timing json = %s, want threshold and windows", string(data))
|
||||
}
|
||||
if !strings.Contains(string(data), "precipitation_type") || !strings.Contains(string(data), "expectation_phrase") {
|
||||
t.Fatalf("precip timing json = %s, want precipitation type and expectation phrase", string(data))
|
||||
}
|
||||
if strings.Contains(string(data), `"start"`) || strings.Contains(string(data), `"end"`) {
|
||||
t.Fatalf("precip timing json = %s, want period_begins/period_ends instead of start/end", string(data))
|
||||
}
|
||||
@@ -136,6 +145,100 @@ func TestPrecipTimingModuleHandlesRainyAndDryForecasts(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrecipTimingModuleBuildsExpectationPhrases(t *testing.T) {
|
||||
now := mustParseModuleTime("2026-05-29T08:00:00-05:00")
|
||||
tests := []struct {
|
||||
name string
|
||||
maxPop float64
|
||||
descriptions []string
|
||||
wantType string
|
||||
wantPhrase string
|
||||
}{
|
||||
{
|
||||
name: "chance lower bound",
|
||||
maxPop: 40,
|
||||
descriptions: []string{"Scattered showers"},
|
||||
wantType: "showers",
|
||||
wantPhrase: "Chance of showers.",
|
||||
},
|
||||
{
|
||||
name: "chance upper bound",
|
||||
maxPop: 49,
|
||||
descriptions: []string{"Rain possible"},
|
||||
wantType: "rain",
|
||||
wantPhrase: "Chance of rain.",
|
||||
},
|
||||
{
|
||||
name: "likely lower bound",
|
||||
maxPop: 50,
|
||||
descriptions: []string{"Drizzle"},
|
||||
wantType: "drizzle",
|
||||
wantPhrase: "Drizzle likely.",
|
||||
},
|
||||
{
|
||||
name: "likely upper bound",
|
||||
maxPop: 69,
|
||||
descriptions: []string{"Freezing rain"},
|
||||
wantType: "freezing rain",
|
||||
wantPhrase: "Freezing rain likely.",
|
||||
},
|
||||
{
|
||||
name: "expect lower bound",
|
||||
maxPop: 70,
|
||||
descriptions: []string{"Snow"},
|
||||
wantType: "snow",
|
||||
wantPhrase: "Expect snow.",
|
||||
},
|
||||
{
|
||||
name: "showers and thunderstorms preferred",
|
||||
maxPop: 100,
|
||||
descriptions: []string{"Showers likely", "Thunderstorms possible"},
|
||||
wantType: "showers and thunderstorms",
|
||||
wantPhrase: "Expect showers and thunderstorms.",
|
||||
},
|
||||
{
|
||||
name: "thunderstorms only",
|
||||
maxPop: 80,
|
||||
descriptions: []string{"Thunderstorms"},
|
||||
wantType: "thunderstorms",
|
||||
wantPhrase: "Expect thunderstorms.",
|
||||
},
|
||||
{
|
||||
name: "unknown fallback",
|
||||
maxPop: 95,
|
||||
descriptions: []string{"Unsettled conditions"},
|
||||
wantType: "precipitation",
|
||||
wantPhrase: "Expect precipitation.",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
value := precipTimingValue(forecast.PrecipTiming{
|
||||
ProbabilityThreshold: forecast.DefaultPrecipWindowProbabilityThreshold,
|
||||
PrecipitationWindows: []forecast.PrecipitationWindow{
|
||||
{
|
||||
Start: now,
|
||||
MaxPrecipitationProbability: forecast.TimedValue{
|
||||
Value: tt.maxPop,
|
||||
Time: now,
|
||||
},
|
||||
ProbabilityThreshold: forecast.DefaultPrecipWindowProbabilityThreshold,
|
||||
TextDescriptions: tt.descriptions,
|
||||
},
|
||||
},
|
||||
}, "America/Chicago")
|
||||
if len(value.PrecipitationWindows) != 1 {
|
||||
t.Fatalf("PrecipitationWindows = %#v, want one window", value.PrecipitationWindows)
|
||||
}
|
||||
window := value.PrecipitationWindows[0]
|
||||
if window.PrecipitationType != tt.wantType || window.ExpectationPhrase != tt.wantPhrase {
|
||||
t.Fatalf("window = %#v, want type %q and phrase %q", window, tt.wantType, tt.wantPhrase)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrecipTimingModuleUsesDerivedTimingWithoutDaypartSummaries(t *testing.T) {
|
||||
registry := MustDefaultModuleRegistry()
|
||||
ctx := derivedModuleContext(report.Hourly)
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
package briefing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
|
||||
)
|
||||
|
||||
const (
|
||||
precipTimingChanceLowerBound = 40
|
||||
precipTimingLikelyLowerBound = 50
|
||||
precipTimingExpectLowerBound = 70
|
||||
)
|
||||
|
||||
type PrecipTimingModule struct {
|
||||
MaxPopPercent *int `json:"max_pop_percent,omitempty"`
|
||||
MaxPopTime string `json:"max_pop_time,omitempty"`
|
||||
@@ -21,6 +30,8 @@ type PrecipitationWindowModule struct {
|
||||
MaxPopPercent *int `json:"max_pop_percent,omitempty"`
|
||||
MaxPopTime string `json:"max_pop_time,omitempty"`
|
||||
MaxPopHourLabel string `json:"max_pop_hour_label,omitempty"`
|
||||
PrecipitationType string `json:"precipitation_type,omitempty"`
|
||||
ExpectationPhrase string `json:"expectation_phrase,omitempty"`
|
||||
}
|
||||
|
||||
func buildPrecipTimingModule(ctx ModuleContext, _ any) (*module.Output, error) {
|
||||
@@ -49,7 +60,50 @@ func precipTimingValue(timing forecast.PrecipTiming, timezone string) PrecipTimi
|
||||
item.MaxPopPercent = roundedInt(&window.MaxPrecipitationProbability.Value)
|
||||
item.MaxPopTime = clockLabel(window.MaxPrecipitationProbability.Time, timezone)
|
||||
item.MaxPopHourLabel = hourMinuteLabel(window.MaxPrecipitationProbability.Time, timezone)
|
||||
item.PrecipitationType = precipitationWindowType(window.TextDescriptions)
|
||||
if item.MaxPopPercent != nil {
|
||||
item.ExpectationPhrase = precipitationWindowExpectationPhrase(*item.MaxPopPercent, item.PrecipitationType)
|
||||
}
|
||||
value.PrecipitationWindows = append(value.PrecipitationWindows, item)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func precipitationWindowType(descriptions []string) string {
|
||||
combined := strings.ToLower(strings.Join(descriptions, " "))
|
||||
switch {
|
||||
case (strings.Contains(combined, "thunderstorm") || strings.Contains(combined, "t-storm")) &&
|
||||
(strings.Contains(combined, "shower") || strings.Contains(combined, "rain")):
|
||||
return "showers and thunderstorms"
|
||||
case strings.Contains(combined, "freezing rain"):
|
||||
return "freezing rain"
|
||||
case strings.Contains(combined, "thunderstorm") || strings.Contains(combined, "t-storm"):
|
||||
return "thunderstorms"
|
||||
case strings.Contains(combined, "shower"):
|
||||
return "showers"
|
||||
case strings.Contains(combined, "snow"):
|
||||
return "snow"
|
||||
case strings.Contains(combined, "drizzle"):
|
||||
return "drizzle"
|
||||
case strings.Contains(combined, "rain"):
|
||||
return "rain"
|
||||
default:
|
||||
return "precipitation"
|
||||
}
|
||||
}
|
||||
|
||||
func precipitationWindowExpectationPhrase(maxPopPercent int, precipitationType string) string {
|
||||
if precipitationType == "" {
|
||||
precipitationType = "precipitation"
|
||||
}
|
||||
switch {
|
||||
case maxPopPercent >= precipTimingExpectLowerBound:
|
||||
return fmt.Sprintf("Expect %s.", precipitationType)
|
||||
case maxPopPercent >= precipTimingLikelyLowerBound:
|
||||
return fmt.Sprintf("%s likely.", sentenceCase(precipitationType))
|
||||
case maxPopPercent >= precipTimingChanceLowerBound:
|
||||
return fmt.Sprintf("Chance of %s.", precipitationType)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ type PrecipitationWindow struct {
|
||||
End *time.Time `json:"end,omitempty"`
|
||||
MaxPrecipitationProbability TimedValue `json:"maxPrecipitationProbability"`
|
||||
ProbabilityThreshold float64 `json:"probabilityThreshold"`
|
||||
TextDescriptions []string `json:"textDescriptions,omitempty"`
|
||||
}
|
||||
|
||||
func BuildPrecipTiming(periods []weatherdata.ForecastPeriod) PrecipTiming {
|
||||
@@ -114,6 +115,7 @@ func buildPrecipTimingWithThreshold(periods []weatherdata.ForecastPeriod, thresh
|
||||
},
|
||||
ProbabilityThreshold: threshold,
|
||||
}
|
||||
appendActiveTextDescription(active, forecastPeriod.TextDescription)
|
||||
activeLastEnd = forecastPeriod.EndTime
|
||||
if timing.FirstPrecipitation == nil {
|
||||
timing.FirstPrecipitation = &TimedValue{
|
||||
@@ -135,6 +137,8 @@ func buildPrecipTimingWithThreshold(periods []weatherdata.ForecastPeriod, thresh
|
||||
if forecastPeriod.StartTime.After(activeLastEnd) {
|
||||
closeActive()
|
||||
startActive(forecastPeriod, probability)
|
||||
} else {
|
||||
appendActiveTextDescription(active, forecastPeriod.TextDescription)
|
||||
}
|
||||
if probability > active.MaxPrecipitationProbability.Value {
|
||||
active.MaxPrecipitationProbability = TimedValue{
|
||||
@@ -166,6 +170,17 @@ func buildPrecipTimingWithThreshold(periods []weatherdata.ForecastPeriod, thresh
|
||||
return timing
|
||||
}
|
||||
|
||||
func appendActiveTextDescription(window *PrecipitationWindow, text string) {
|
||||
if window == nil {
|
||||
return
|
||||
}
|
||||
text = strings.TrimSpace(text)
|
||||
if text == "" {
|
||||
return
|
||||
}
|
||||
window.TextDescriptions = append(window.TextDescriptions, text)
|
||||
}
|
||||
|
||||
func BuildDailySummary(bundle *weatherdata.Bundle, date time.Time, location *time.Location, dayparts []DaypartDefinition) (*DailySummary, error) {
|
||||
if bundle == nil {
|
||||
return nil, fmt.Errorf("forecast bundle is required")
|
||||
|
||||
@@ -245,10 +245,16 @@ func TestBuildPrecipTimingBuildsThresholdWindows(t *testing.T) {
|
||||
if first.MaxPrecipitationProbability.Value != 60 || first.MaxPrecipitationProbability.Time.Format(time.RFC3339) != "2026-05-29T10:00:00-05:00" {
|
||||
t.Fatalf("first window max = %#v, want 60 at 10 AM", first.MaxPrecipitationProbability)
|
||||
}
|
||||
if len(first.TextDescriptions) != 2 || first.TextDescriptions[0] != "Showers" || first.TextDescriptions[1] != "Rain likely" {
|
||||
t.Fatalf("first window text descriptions = %#v, want contributing hourly descriptions", first.TextDescriptions)
|
||||
}
|
||||
second := timing.PrecipitationWindows[1]
|
||||
if second.Start.Format(time.RFC3339) != "2026-05-29T12:00:00-05:00" || second.End == nil || second.End.Format(time.RFC3339) != "2026-05-29T13:00:00-05:00" {
|
||||
t.Fatalf("second window = %#v, want noon-1 PM", second)
|
||||
}
|
||||
if len(second.TextDescriptions) != 1 || second.TextDescriptions[0] != "Thunderstorms" {
|
||||
t.Fatalf("second window text descriptions = %#v, want thunderstorm description", second.TextDescriptions)
|
||||
}
|
||||
if !timing.ThunderMentioned {
|
||||
t.Fatal("ThunderMentioned = false, want true")
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ func TestBuildTodayRenderContext(t *testing.T) {
|
||||
"- **Morning:** Partly cloudy, with temperatures in the low 60s.",
|
||||
"- **Afternoon:** Showers, with temperatures in the mid 70s. Chance of precipitation is 70%.",
|
||||
"## Precipitation Timing",
|
||||
"- **3:00 PM** to **6:00 PM**: Precipitation is expected during this period. The peak precipitation chance is 70% at 3:00 PM.",
|
||||
"- **3:00 PM** to **6:00 PM**: Expect showers. The peak precipitation chance is 70% at 3:00 PM.",
|
||||
"The most likely rain window is from midafternoon into early evening.",
|
||||
"Morning conditions should stay mostly dry.",
|
||||
"Rain chances increase during the afternoon as deeper moisture arrives.",
|
||||
@@ -542,7 +542,7 @@ func TestBuildDailyRenderContext(t *testing.T) {
|
||||
"- **Afternoon:** Showers, with temperatures in the mid 70s. Chance of precipitation is 70%.",
|
||||
"- **Evening:** Mostly cloudy, with temperatures in the upper 60s.",
|
||||
"## Precipitation Timing",
|
||||
"- **3:00 PM** to **6:00 PM**: Precipitation is expected during this period. The peak precipitation chance is 70% at 3:00 PM.",
|
||||
"- **3:00 PM** to **6:00 PM**: Expect showers. The peak precipitation chance is 70% at 3:00 PM.",
|
||||
"The most likely rain window is from midafternoon into early evening.",
|
||||
"Morning conditions should stay mostly dry.",
|
||||
"Rain chances increase during the afternoon as deeper moisture arrives.",
|
||||
@@ -663,7 +663,7 @@ func TestBuildTomorrowRenderContext(t *testing.T) {
|
||||
"- **Morning:** Partly cloudy, with temperatures in the low 60s.",
|
||||
"- **Afternoon:** Showers, with temperatures in the mid 70s. Chance of precipitation is 70%.",
|
||||
"## Precipitation Timing",
|
||||
"- **3:00 PM** to **6:00 PM**: Precipitation is expected during this period. The peak precipitation chance is 70% at 3:00 PM.",
|
||||
"- **3:00 PM** to **6:00 PM**: Expect showers. The peak precipitation chance is 70% at 3:00 PM.",
|
||||
"The most likely rain window is from midafternoon into early evening.",
|
||||
"Morning conditions should stay mostly dry.",
|
||||
"Rain chances increase during the afternoon as deeper moisture arrives.",
|
||||
@@ -910,7 +910,7 @@ func testTodaySnapshot(t *testing.T) module.Snapshot {
|
||||
MaxPopPercent: intPtr(70),
|
||||
MaxPopTime: "3 PM",
|
||||
PrecipitationWindows: []briefing.PrecipitationWindowModule{
|
||||
{PeriodBeginsHourLabel: "3:00 PM", PeriodEndsHourLabel: "6:00 PM", MaxPopPercent: intPtr(70), MaxPopHourLabel: "3:00 PM"},
|
||||
{PeriodBeginsHourLabel: "3:00 PM", PeriodEndsHourLabel: "6:00 PM", MaxPopPercent: intPtr(70), MaxPopHourLabel: "3:00 PM", PrecipitationType: "showers", ExpectationPhrase: "Expect showers."},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1045,7 +1045,7 @@ func testDailySnapshot(t *testing.T) module.Snapshot {
|
||||
MaxPopPercent: intPtr(70),
|
||||
MaxPopTime: "3 PM",
|
||||
PrecipitationWindows: []briefing.PrecipitationWindowModule{
|
||||
{PeriodBeginsHourLabel: "3:00 PM", PeriodEndsHourLabel: "6:00 PM", MaxPopPercent: intPtr(70), MaxPopHourLabel: "3:00 PM"},
|
||||
{PeriodBeginsHourLabel: "3:00 PM", PeriodEndsHourLabel: "6:00 PM", MaxPopPercent: intPtr(70), MaxPopHourLabel: "3:00 PM", PrecipitationType: "showers", ExpectationPhrase: "Expect showers."},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1317,7 +1317,7 @@ func testTomorrowSnapshot(t *testing.T) module.Snapshot {
|
||||
MaxPopPercent: intPtr(70),
|
||||
MaxPopTime: "3 PM",
|
||||
PrecipitationWindows: []briefing.PrecipitationWindowModule{
|
||||
{PeriodBeginsHourLabel: "3:00 PM", PeriodEndsHourLabel: "6:00 PM", MaxPopPercent: intPtr(70), MaxPopHourLabel: "3:00 PM"},
|
||||
{PeriodBeginsHourLabel: "3:00 PM", PeriodEndsHourLabel: "6:00 PM", MaxPopPercent: intPtr(70), MaxPopHourLabel: "3:00 PM", PrecipitationType: "showers", ExpectationPhrase: "Expect showers."},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -348,7 +348,7 @@ func TestRenderTomorrow(t *testing.T) {
|
||||
},
|
||||
PrecipTiming: &testPrecipTiming{
|
||||
PrecipitationWindows: []testPrecipWindow{
|
||||
{PeriodBeginsHourLabel: "3:00 PM", PeriodEndsHourLabel: "6:00 PM", MaxPopPercent: intPtr(70), MaxPopHourLabel: "3:00 PM"},
|
||||
{PeriodBeginsHourLabel: "3:00 PM", PeriodEndsHourLabel: "6:00 PM", MaxPopPercent: intPtr(70), MaxPopHourLabel: "3:00 PM", ExpectationPhrase: "Expect showers."},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -365,7 +365,7 @@ func TestRenderTomorrow(t *testing.T) {
|
||||
"- **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%.",
|
||||
"- **3:00 PM** to **6:00 PM**: Precipitation is expected during this period. The peak precipitation chance is 70% at 3:00 PM.",
|
||||
"- **3:00 PM** to **6:00 PM**: Expect showers. The peak precipitation chance is 70% at 3:00 PM.",
|
||||
"A few showers may linger into early evening.",
|
||||
"Clouds increase after sunrise.",
|
||||
"Rain chances peak during the afternoon.",
|
||||
@@ -439,7 +439,7 @@ func TestRenderDaily(t *testing.T) {
|
||||
},
|
||||
PrecipTiming: &testPrecipTiming{
|
||||
PrecipitationWindows: []testPrecipWindow{
|
||||
{PeriodBeginsHourLabel: "3:00 PM", PeriodEndsHourLabel: "6:00 PM", MaxPopPercent: intPtr(70), MaxPopHourLabel: "3:00 PM"},
|
||||
{PeriodBeginsHourLabel: "3:00 PM", PeriodEndsHourLabel: "6:00 PM", MaxPopPercent: intPtr(70), MaxPopHourLabel: "3:00 PM", ExpectationPhrase: "Expect showers."},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -455,7 +455,7 @@ func TestRenderDaily(t *testing.T) {
|
||||
"The selected day starts dry before showers return later in the day.",
|
||||
"- **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**: Precipitation is expected during this period. The peak precipitation chance is 70% at 3:00 PM.",
|
||||
"- **3:00 PM** to **6:00 PM**: Expect showers. The peak precipitation chance is 70% at 3:00 PM.",
|
||||
"A few showers may linger into early evening.",
|
||||
"Clouds increase after sunrise.",
|
||||
"Rain chances peak during the afternoon.",
|
||||
@@ -532,7 +532,7 @@ func TestRenderToday(t *testing.T) {
|
||||
},
|
||||
PrecipTiming: &testPrecipTiming{
|
||||
PrecipitationWindows: []testPrecipWindow{
|
||||
{PeriodBeginsHourLabel: "3:00 PM", PeriodEndsHourLabel: "6:00 PM", MaxPopPercent: intPtr(70), MaxPopHourLabel: "3:00 PM"},
|
||||
{PeriodBeginsHourLabel: "3:00 PM", PeriodEndsHourLabel: "6:00 PM", MaxPopPercent: intPtr(70), MaxPopHourLabel: "3:00 PM", ExpectationPhrase: "Expect showers."},
|
||||
},
|
||||
},
|
||||
TodayPlanning: &testTodayPlanning{
|
||||
@@ -553,7 +553,7 @@ func TestRenderToday(t *testing.T) {
|
||||
"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%.",
|
||||
"- **3:00 PM** to **6:00 PM**: Precipitation is expected during this period. The peak precipitation chance is 70% at 3:00 PM.",
|
||||
"- **3:00 PM** to **6:00 PM**: Expect showers. The peak precipitation chance is 70% at 3:00 PM.",
|
||||
"A few showers may linger into early evening.",
|
||||
"Clouds increase after sunrise.",
|
||||
"Rain chances peak during the afternoon.",
|
||||
@@ -943,6 +943,8 @@ type testPrecipWindow struct {
|
||||
MaxPopPercent *int
|
||||
MaxPopTime string
|
||||
MaxPopHourLabel string
|
||||
PrecipitationType string
|
||||
ExpectationPhrase string
|
||||
}
|
||||
|
||||
type testAlertDigest struct {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{{ define "precipitation_timing" }}{{ with .Modules.PrecipTiming }}{{ with .PrecipitationWindows }}
|
||||
## Precipitation Timing
|
||||
{{ range . }}{{ $window := . }}
|
||||
- **{{ if .PeriodBeginsHourLabel }}{{ .PeriodBeginsHourLabel }}{{ else }}{{ .PeriodBegins }}{{ end }}**{{ with .PeriodEndsHourLabel }} to **{{ . }}**{{ else }}{{ with .PeriodEnds }} to **{{ . }}**{{ end }}{{ end }}: Precipitation is expected during this period.{{ with .MaxPopPercent }} The peak precipitation chance is {{ . }}%{{ with $window.MaxPopHourLabel }} at {{ . }}{{ else }}{{ with $window.MaxPopTime }} at {{ . }}{{ end }}{{ end }}.{{ end }}
|
||||
- **{{ if .PeriodBeginsHourLabel }}{{ .PeriodBeginsHourLabel }}{{ else }}{{ .PeriodBegins }}{{ end }}**{{ with .PeriodEndsHourLabel }} to **{{ . }}**{{ else }}{{ with .PeriodEnds }} to **{{ . }}**{{ end }}{{ end }}: {{ with .ExpectationPhrase }}{{ . }}{{ else }}Chance of precipitation.{{ end }}{{ with .MaxPopPercent }} The peak precipitation chance is {{ . }}%{{ with $window.MaxPopHourLabel }} at {{ . }}{{ else }}{{ with $window.MaxPopTime }} at {{ . }}{{ end }}{{ end }}.{{ end }}
|
||||
{{ end }}{{ with $.GeneratedText.PrecipitationTiming }}
|
||||
{{ . }}
|
||||
{{ end }}
|
||||
|
||||
Reference in New Issue
Block a user