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"` ProbabilityThreshold float64 `json:"probability_threshold"` PrecipitationWindows []PrecipitationWindowModule `json:"precipitation_windows,omitempty"` ThunderMentioned bool `json:"thunder_mentioned"` } type PrecipitationWindowModule struct { PeriodBegins string `json:"period_begins"` PeriodBeginsHourLabel string `json:"period_begins_hour_label,omitempty"` PeriodEnds string `json:"period_ends,omitempty"` PeriodEndsHourLabel string `json:"period_ends_hour_label,omitempty"` 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) { value := precipTimingValue(ctx.Derived.PrecipTiming, ctx.Timezone) return &module.Output{ID: module.PrecipTiming, StanzaName: "precip_timing", Value: value}, nil } func precipTimingValue(timing forecast.PrecipTiming, timezone string) PrecipTimingModule { value := PrecipTimingModule{ ProbabilityThreshold: timing.ProbabilityThreshold, ThunderMentioned: timing.ThunderMentioned, } if timing.MaxPrecipitationProbability != nil { value.MaxPopPercent = roundedInt(&timing.MaxPrecipitationProbability.Value) value.MaxPopTime = clockLabel(timing.MaxPrecipitationProbability.Time, timezone) } for _, window := range timing.PrecipitationWindows { item := PrecipitationWindowModule{ PeriodBegins: friendlyDateTimeLabel(window.Start, timezone), PeriodBeginsHourLabel: hourMinuteLabel(window.Start, timezone), } if window.End != nil { item.PeriodEnds = friendlyDateTimeLabel(*window.End, timezone) item.PeriodEndsHourLabel = hourMinuteLabel(*window.End, timezone) } 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.", capitalizeFirst(strings.TrimSpace(precipitationType))) case maxPopPercent >= precipTimingChanceLowerBound: return fmt.Sprintf("Chance of %s.", precipitationType) default: return "" } }