Update the shared precipitation timing template
This commit is contained in:
@@ -76,9 +76,9 @@ fail rendering instead of producing incomplete Markdown.
|
|||||||
|
|
||||||
Daily and Tomorrow call the shared `daypart_forecast` partial. Today calls
|
Daily and Tomorrow call the shared `daypart_forecast` partial. Today calls
|
||||||
`today_daypart_forecast` so it can omit elapsed or missing dayparts. Daily,
|
`today_daypart_forecast` so it can omit elapsed or missing dayparts. Daily,
|
||||||
Today, and Tomorrow call the shared `precipitation_timing` partial. Partial
|
Today, Tomorrow, and Hourly call the shared `precipitation_timing` partial.
|
||||||
files are parsed with each top-level template at render time and receive the
|
Partial files are parsed with each top-level template at render time and
|
||||||
same typed render context as the caller.
|
receive the same typed render context as the caller.
|
||||||
|
|
||||||
## Schema Contract
|
## Schema Contract
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ Shared named partials live under `internal/reporttemplate/templates/partials/`:
|
|||||||
|
|
||||||
- `daypart_forecast.md.tmpl`, used by Daily and Tomorrow
|
- `daypart_forecast.md.tmpl`, used by Daily and Tomorrow
|
||||||
- `today_daypart_forecast.md.tmpl`, used by Today
|
- `today_daypart_forecast.md.tmpl`, used by Today
|
||||||
- `precipitation_timing.md.tmpl`, used by Daily, Today, and Tomorrow
|
- `precipitation_timing.md.tmpl`, used by Daily, Today, Tomorrow, and Hourly
|
||||||
|
|
||||||
Templates are rendered from structured contexts such as `DailyRenderContext`,
|
Templates are rendered from structured contexts such as `DailyRenderContext`,
|
||||||
`TodayRenderContext`, `TomorrowRenderContext`, and `HourlyRenderContext`.
|
`TodayRenderContext`, `TomorrowRenderContext`, and `HourlyRenderContext`.
|
||||||
@@ -294,7 +294,7 @@ Common fields:
|
|||||||
| `.Modules.PrecipTiming.PrecipitationWindows[].MaxPopTime` | string | Friendly local time for the window maximum. |
|
| `.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[].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[].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.PrecipitationWindows[].ExpectationPhrase` | string | Probability-based sentence used by precipitation timing templates. |
|
||||||
| `.Modules.PrecipTiming.ThunderMentioned` | bool | Whether thunder is mentioned in the forecast text. |
|
| `.Modules.PrecipTiming.ThunderMentioned` | bool | Whether thunder is mentioned in the forecast text. |
|
||||||
|
|
||||||
### Daypart Summaries
|
### Daypart Summaries
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ func TestRenderHourly(t *testing.T) {
|
|||||||
MaxPopPercent: intPtr(70),
|
MaxPopPercent: intPtr(70),
|
||||||
MaxPopTime: "10 AM",
|
MaxPopTime: "10 AM",
|
||||||
PrecipitationWindows: []testPrecipWindow{
|
PrecipitationWindows: []testPrecipWindow{
|
||||||
{PeriodBegins: "10 AM", PeriodBeginsHourLabel: "10:00 AM", PeriodEnds: "12 PM", PeriodEndsHourLabel: "12:00 PM", MaxPopPercent: intPtr(70), MaxPopTime: "10 AM", MaxPopHourLabel: "10:00 AM"},
|
{PeriodBegins: "10 AM", PeriodBeginsHourLabel: "10:00 AM", PeriodEnds: "12 PM", PeriodEndsHourLabel: "12:00 PM", MaxPopPercent: intPtr(70), MaxPopTime: "10 AM", MaxPopHourLabel: "10:00 AM", ExpectationPhrase: "Expect showers."},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AlertDigest: &testAlertDigest{
|
AlertDigest: &testAlertDigest{
|
||||||
@@ -272,7 +272,7 @@ func TestRenderHourly(t *testing.T) {
|
|||||||
"- **Flood Watch**: Flood Watch until 2:30 PM",
|
"- **Flood Watch**: Flood Watch until 2:30 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**: Precipitation is expected during this period. 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.",
|
||||||
"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.",
|
||||||
} {
|
} {
|
||||||
@@ -732,6 +732,48 @@ func TestRenderHourlyOmitsConditionalSectionsForClearWeather(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRenderHourlyUsesSharedOpenEndedPrecipitationTiming(t *testing.T) {
|
||||||
|
rendered, err := Render("hourly", testRenderContext{
|
||||||
|
Report: testReportContext{
|
||||||
|
Title: "Hourly Report",
|
||||||
|
GeneratedAtLabel: "Saturday, June 14, 2026 at 9:14 AM",
|
||||||
|
},
|
||||||
|
GeneratedText: testGeneratedText{
|
||||||
|
Summary: "Rain chances increase through midday.",
|
||||||
|
ForecastDiscussion: "Showers may continue beyond the report period.",
|
||||||
|
PrecipitationTiming: "Plan for wet roads through the end of the period.",
|
||||||
|
},
|
||||||
|
Modules: testModules{
|
||||||
|
CurrentConditions: &testCurrentConditions{},
|
||||||
|
HourlyForecast: &testHourlyForecast{},
|
||||||
|
AlertDigest: &testAlertDigest{},
|
||||||
|
PrecipTiming: &testPrecipTiming{
|
||||||
|
PrecipitationWindows: []testPrecipWindow{
|
||||||
|
{
|
||||||
|
PeriodBeginsHourLabel: "8:00 AM",
|
||||||
|
MaxPopPercent: intPtr(60),
|
||||||
|
MaxPopHourLabel: "10:00 AM",
|
||||||
|
ExpectationPhrase: "Showers likely.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Render() error = %v", err)
|
||||||
|
}
|
||||||
|
text := string(rendered)
|
||||||
|
for _, want := range []string{
|
||||||
|
"## Precipitation Timing",
|
||||||
|
"- **Starting at 8:00 AM**: Showers likely. The peak precipitation chance is 60% at 10:00 AM.",
|
||||||
|
"Plan for wet roads through the end of the period.",
|
||||||
|
} {
|
||||||
|
if !strings.Contains(text, want) {
|
||||||
|
t.Fatalf("rendered template missing %q:\n%s", want, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUnknownAssetsReturnActionableErrors(t *testing.T) {
|
func TestUnknownAssetsReturnActionableErrors(t *testing.T) {
|
||||||
if _, err := Template("missing"); err == nil || !strings.Contains(err.Error(), `unknown report template "missing"`) {
|
if _, err := Template("missing"); err == nil || !strings.Contains(err.Error(), `unknown report template "missing"`) {
|
||||||
t.Fatalf("Template() error = %v, want unknown template", err)
|
t.Fatalf("Template() error = %v, want unknown template", err)
|
||||||
|
|||||||
@@ -26,14 +26,7 @@ Current conditions are unavailable.
|
|||||||
- No hourly forecast rows are available.
|
- No hourly forecast rows are available.
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ with .Modules.PrecipTiming }}{{ with .PrecipitationWindows }}
|
{{ template "precipitation_timing" . }}
|
||||||
## 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 }}
|
|
||||||
{{ end }}{{ with $.GeneratedText.PrecipitationTiming }}
|
|
||||||
{{ . }}
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}{{ end }}
|
|
||||||
## Forecast Discussion
|
## Forecast Discussion
|
||||||
|
|
||||||
{{ .GeneratedText.ForecastDiscussion }}
|
{{ .GeneratedText.ForecastDiscussion }}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{{ define "precipitation_timing" }}{{ with .Modules.PrecipTiming }}{{ with .PrecipitationWindows }}
|
{{ define "precipitation_timing" }}{{ with .Modules.PrecipTiming }}{{ with .PrecipitationWindows }}
|
||||||
## Precipitation Timing
|
## Precipitation Timing
|
||||||
{{ range . }}{{ $window := . }}
|
{{ range . }}{{ $window := . }}
|
||||||
- **{{ 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 }}
|
- **{{ if or .PeriodEndsHourLabel .PeriodEnds }}{{ if .PeriodBeginsHourLabel }}{{ .PeriodBeginsHourLabel }}{{ else }}{{ .PeriodBegins }}{{ end }}{{ else }}Starting at {{ if .PeriodBeginsHourLabel }}{{ .PeriodBeginsHourLabel }}{{ else }}{{ .PeriodBegins }}{{ end }}{{ 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 }}{{ with $.GeneratedText.PrecipitationTiming }}
|
||||||
{{ . }}
|
{{ . }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
Reference in New Issue
Block a user