130 lines
4.3 KiB
Go
130 lines
4.3 KiB
Go
package briefing
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
|
|
)
|
|
|
|
type ThreeDay struct {
|
|
Days []OutlookDay `json:"days"`
|
|
RelevantAlerts []forecast.AlertOverlap `json:"relevantAlerts,omitempty"`
|
|
Discussion DiscussionContext `json:"discussion,omitempty"`
|
|
WeatherStory *WeatherStoryContext `json:"weatherStory,omitempty"`
|
|
}
|
|
|
|
type OutlookDay struct {
|
|
Date string `json:"date"`
|
|
Period timeutil.Period `json:"period"`
|
|
OverallCharacter string `json:"overallCharacter"`
|
|
Temperature forecast.Range `json:"temperature,omitempty"`
|
|
MaxPrecipitationProbability *forecast.TimedValue `json:"maxPrecipitationProbability,omitempty"`
|
|
PeakWindGust *forecast.TimedValue `json:"peakWindGust,omitempty"`
|
|
Risks []string `json:"risks,omitempty"`
|
|
OutdoorWindows OutdoorWindows `json:"outdoorWindows"`
|
|
RelevantAlerts []forecast.AlertOverlap `json:"relevantAlerts,omitempty"`
|
|
Dayparts []forecast.DaypartSummary `json:"dayparts"`
|
|
}
|
|
|
|
func BuildThreeDay(ctx BuildContext, summaries []forecast.DailySummary) (Package, error) {
|
|
if ctx.Resolved.Definition.ID != report.ThreeDay {
|
|
return Package{}, fmt.Errorf("3-day briefing requires a 3-day report definition")
|
|
}
|
|
if len(summaries) == 0 {
|
|
return Package{}, fmt.Errorf("3-day forecast summaries are required")
|
|
}
|
|
pkg := Package{
|
|
Metadata: BuildMetadata(ctx),
|
|
ThreeDay: &ThreeDay{
|
|
Discussion: buildDiscussion(summaries[0].Discussion),
|
|
WeatherStory: buildWeatherStory(ctx.Bundle),
|
|
},
|
|
}
|
|
for _, summary := range summaries {
|
|
day := buildOutlookDay(summary)
|
|
pkg.ThreeDay.Days = append(pkg.ThreeDay.Days, day)
|
|
}
|
|
pkg.ThreeDay.RelevantAlerts = collectOutlookAlerts(pkg.ThreeDay.Days)
|
|
return pkg, nil
|
|
}
|
|
|
|
func collectOutlookAlerts(days []OutlookDay) []forecast.AlertOverlap {
|
|
alerts := map[string]forecast.AlertOverlap{}
|
|
for _, day := range days {
|
|
for _, alert := range day.RelevantAlerts {
|
|
key := alert.Event
|
|
if key == "" {
|
|
key = alert.Headline
|
|
}
|
|
if key != "" {
|
|
alerts[key] = alert
|
|
}
|
|
}
|
|
}
|
|
keys := make([]string, 0, len(alerts))
|
|
for key := range alerts {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
out := make([]forecast.AlertOverlap, 0, len(keys))
|
|
for _, key := range keys {
|
|
out = append(out, alerts[key])
|
|
}
|
|
return out
|
|
}
|
|
|
|
func buildOutlookDay(summary forecast.DailySummary) OutlookDay {
|
|
day := OutlookDay{
|
|
Date: summary.Date,
|
|
Period: summary.Period,
|
|
OutdoorWindows: buildOutdoorWindows(summary.Dayparts),
|
|
RelevantAlerts: summary.AlertOverlaps,
|
|
Dayparts: summary.Dayparts,
|
|
}
|
|
conditions := map[string]struct{}{}
|
|
risks := map[string]struct{}{}
|
|
for _, daypart := range summary.Dayparts {
|
|
addRange(&day.Temperature, daypart.Temperature)
|
|
maxTimedValue(&day.MaxPrecipitationProbability, daypart.MaxPrecipitationProbability)
|
|
maxTimedValue(&day.PeakWindGust, daypart.PeakWindGust)
|
|
if daypart.DominantCondition != "" {
|
|
conditions[daypart.DominantCondition] = struct{}{}
|
|
}
|
|
for _, risk := range hazardsForIndicators(daypart.Indicators) {
|
|
risks[risk] = struct{}{}
|
|
}
|
|
if daypart.MaxPrecipitationProbability != nil && daypart.MaxPrecipitationProbability.Value >= 50 {
|
|
risks["precipitation"] = struct{}{}
|
|
}
|
|
if daypart.PeakWindGust != nil && daypart.PeakWindGust.Value >= 30 {
|
|
risks["wind"] = struct{}{}
|
|
}
|
|
}
|
|
for _, alert := range summary.AlertOverlaps {
|
|
if alert.Event != "" {
|
|
risks[alert.Event] = struct{}{}
|
|
}
|
|
}
|
|
day.Risks = sortedSet(risks)
|
|
day.OverallCharacter = outlookCharacter(sortedSet(conditions), day.Risks)
|
|
return day
|
|
}
|
|
|
|
func outlookCharacter(conditions []string, risks []string) string {
|
|
if len(conditions) == 0 && len(risks) == 0 {
|
|
return "Quiet weather is expected."
|
|
}
|
|
parts := []string{}
|
|
if len(conditions) > 0 {
|
|
parts = append(parts, strings.Join(conditions, "; "))
|
|
}
|
|
if len(risks) > 0 {
|
|
parts = append(parts, "risks: "+strings.Join(risks, "; "))
|
|
}
|
|
return strings.Join(parts, ". ") + "."
|
|
}
|