145 lines
4.7 KiB
Go
145 lines
4.7 KiB
Go
package changes
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
|
|
)
|
|
|
|
func CompareThreeDay(previous module.Snapshot, current module.Snapshot, thresholds Thresholds) ([]Change, error) {
|
|
previousDayparts, err := requiredStanza[map[string]daypartSummaryStanza](previous, "derived_daypart_summaries")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("previous daypart summaries: %w", err)
|
|
}
|
|
currentDayparts, err := requiredStanza[map[string]daypartSummaryStanza](current, "derived_daypart_summaries")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("current daypart summaries: %w", err)
|
|
}
|
|
previousDays := outlookDaysFromDayparts(previousDayparts)
|
|
currentDays := outlookDaysFromDayparts(currentDayparts)
|
|
return compareOutlookDays(previousDays, currentDays, thresholds, "")
|
|
}
|
|
|
|
type outlookDay struct {
|
|
Date string
|
|
LowTempF *int
|
|
HighTempF *int
|
|
MaxPopPercent *int
|
|
MaxPopTime string
|
|
MaxWindGustMph *int
|
|
Indicators indicators
|
|
}
|
|
|
|
func compareOutlookDays(previousDays map[string]outlookDay, currentDays map[string]outlookDay, thresholds Thresholds, prefix string) ([]Change, error) {
|
|
var changes []Change
|
|
for date, currentDay := range currentDays {
|
|
previousDay, ok := previousDays[date]
|
|
if !ok {
|
|
changes = append(changes, Change{Type: prefix + "outlook_day_added", Message: fmt.Sprintf("Outlook day added: %s.", date), Current: date})
|
|
continue
|
|
}
|
|
changes = append(changes, compareOutlookDay(date, previousDay, currentDay, thresholds, prefix)...)
|
|
}
|
|
for date := range previousDays {
|
|
if _, ok := currentDays[date]; !ok {
|
|
changes = append(changes, Change{Type: prefix + "outlook_day_removed", Message: fmt.Sprintf("Outlook day removed: %s.", date), Previous: date})
|
|
}
|
|
}
|
|
sortChanges(changes)
|
|
return changes, nil
|
|
}
|
|
|
|
func compareOutlookDay(date string, previous outlookDay, current outlookDay, thresholds Thresholds, prefix string) []Change {
|
|
var changes []Change
|
|
for _, change := range compareTemperatureValues("Low", previous.LowTempF, current.LowTempF, thresholds.TemperatureDegrees) {
|
|
change.Message = date + ": " + change.Message
|
|
change.Type = prefix + "outlook_" + change.Type
|
|
changes = append(changes, change)
|
|
}
|
|
for _, change := range compareTemperatureValues("High", previous.HighTempF, current.HighTempF, thresholds.TemperatureDegrees) {
|
|
change.Message = date + ": " + change.Message
|
|
change.Type = prefix + "outlook_" + change.Type
|
|
changes = append(changes, change)
|
|
}
|
|
for _, change := range comparePrecipitationValues(previous.MaxPopPercent, current.MaxPopPercent, thresholds.PrecipProbabilityPoints, prefix+"outlook_") {
|
|
change.Message = date + ": " + change.Message
|
|
changes = append(changes, change)
|
|
}
|
|
for _, change := range comparePrecipTiming(previous.MaxPopTime, current.MaxPopTime, thresholds.PrecipTimingShiftMinutes, prefix+"outlook_") {
|
|
change.Message = date + ": " + change.Message
|
|
changes = append(changes, change)
|
|
}
|
|
for _, change := range compareWindValues(previous.MaxWindGustMph, current.MaxWindGustMph, thresholds.WindGustMilesPerHour, prefix+"outlook_") {
|
|
change.Message = date + ": " + change.Message
|
|
changes = append(changes, change)
|
|
}
|
|
for _, change := range compareIndicators(previous.Indicators, current.Indicators, prefix+"outlook_") {
|
|
change.Message = date + ": " + change.Message
|
|
changes = append(changes, change)
|
|
}
|
|
return changes
|
|
}
|
|
|
|
func outlookDaysFromDayparts(dayparts map[string]daypartSummaryStanza) map[string]outlookDay {
|
|
out := map[string]outlookDay{}
|
|
var keys []string
|
|
for key := range dayparts {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
for _, key := range keys {
|
|
daypart := dayparts[key]
|
|
date := daypartDate(daypart)
|
|
if date == "" {
|
|
continue
|
|
}
|
|
day := out[date]
|
|
day.Date = date
|
|
low, high := parseTempRange(daypart.TempRangeF)
|
|
day.LowTempF = minInt(day.LowTempF, low)
|
|
day.HighTempF = maxInt(day.HighTempF, high)
|
|
day.MaxPopPercent = maxInt(day.MaxPopPercent, daypart.MaxPopPercent)
|
|
if daypart.MaxPopPercent != nil && day.MaxPopPercent != nil && *daypart.MaxPopPercent == *day.MaxPopPercent {
|
|
day.MaxPopTime = daypart.MaxPopTime
|
|
}
|
|
day.MaxWindGustMph = maxInt(day.MaxWindGustMph, daypart.MaxWindGustMph)
|
|
day.Indicators.Snow = day.Indicators.Snow || daypart.Snow
|
|
day.Indicators.Ice = day.Indicators.Ice || daypart.Ice
|
|
out[date] = day
|
|
}
|
|
return out
|
|
}
|
|
|
|
func daypartDate(daypart daypartSummaryStanza) string {
|
|
return daypart.Date
|
|
}
|
|
|
|
func minInt(a *int, b *int) *int {
|
|
if a == nil {
|
|
return copyInt(b)
|
|
}
|
|
if b != nil && *b < *a {
|
|
return copyInt(b)
|
|
}
|
|
return a
|
|
}
|
|
|
|
func maxInt(a *int, b *int) *int {
|
|
if a == nil {
|
|
return copyInt(b)
|
|
}
|
|
if b != nil && *b > *a {
|
|
return copyInt(b)
|
|
}
|
|
return a
|
|
}
|
|
|
|
func copyInt(value *int) *int {
|
|
if value == nil {
|
|
return nil
|
|
}
|
|
copied := *value
|
|
return &copied
|
|
}
|