package changes import ( "fmt" "sort" "gitea.maximumdirect.net/eric/weatherreporter/internal/module" "gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil" ) 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 { if !daypart.Period.Start.IsZero() { return daypart.Period.Start.Format(timeutil.DateLayout) } return "" } 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 }