package briefing import ( "time" "gitea.maximumdirect.net/eric/weatherreporter/internal/module" "gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil" "gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata" ) type NarrativeForecastModule struct { Product string `json:"product,omitempty"` IssuedAt time.Time `json:"issued_at,omitempty"` UpdatedAt *time.Time `json:"updated_at,omitempty"` SourceLocation string `json:"source_location,omitempty"` SourceLocationID string `json:"source_location_id,omitempty"` Periods []NarrativeForecastPeriod `json:"periods,omitempty"` } type NarrativeForecastPeriod struct { Name string `json:"name,omitempty"` PeriodBegins string `json:"period_begins,omitempty"` PeriodEnds string `json:"period_ends,omitempty"` IsDay *bool `json:"is_day,omitempty"` TextDescription string `json:"text_description,omitempty"` TemperatureC *float64 `json:"temperature_c,omitempty"` TemperatureF *float64 `json:"temperature_f,omitempty"` TemperatureCMin *float64 `json:"temperature_c_min,omitempty"` TemperatureFMin *float64 `json:"temperature_f_min,omitempty"` TemperatureCMax *float64 `json:"temperature_c_max,omitempty"` TemperatureFMax *float64 `json:"temperature_f_max,omitempty"` WindSpeedKmh *float64 `json:"wind_speed_kmh,omitempty"` WindSpeedMph *float64 `json:"wind_speed_mph,omitempty"` WindGustKmh *float64 `json:"wind_gust_kmh,omitempty"` WindGustMph *float64 `json:"wind_gust_mph,omitempty"` WindDirection string `json:"wind_direction,omitempty"` ProbabilityOfPrecipitationPercent *float64 `json:"probability_of_precipitation_percent,omitempty"` } func buildNarrativeForecastModule(ctx ModuleContext, _ any) (*module.Output, error) { narrative := ctx.Collected.Narrative if narrative == nil || len(ctx.Derived.ValidPeriodNarrativePeriods) == 0 { return nil, nil } value := NarrativeForecastModule{ Product: narrative.Product, IssuedAt: narrative.IssuedAt, UpdatedAt: copyTime(narrative.UpdatedAt), SourceLocation: narrative.LocationName, SourceLocationID: narrative.LocationID, Periods: narrativeForecastPeriods(ctx.Derived.ValidPeriodNarrativePeriods, ctx.Timezone), } if value.isEmpty() { return nil, nil } return &module.Output{ID: module.NarrativeForecast, StanzaName: "narrative_forecast", Value: value}, nil } func narrativeForecastPeriods(periods []weatherdata.ForecastPeriod, timezone string) []NarrativeForecastPeriod { out := make([]NarrativeForecastPeriod, 0, len(periods)) for _, period := range periods { validPeriod := timeutil.Period{Start: period.StartTime, End: period.EndTime} out = append(out, NarrativeForecastPeriod{ Name: period.Name, PeriodBegins: friendlyPeriodBeginsLabel(validPeriod, timezone), PeriodEnds: friendlyPeriodEndsLabel(validPeriod, timezone), IsDay: copyBool(period.IsDay), TextDescription: period.TextDescription, TemperatureC: copyFloat(period.TemperatureC), TemperatureF: copyFloat(period.TemperatureF), TemperatureCMin: copyFloat(period.TemperatureCMin), TemperatureFMin: copyFloat(period.TemperatureFMin), TemperatureCMax: copyFloat(period.TemperatureCMax), TemperatureFMax: copyFloat(period.TemperatureFMax), WindSpeedKmh: copyFloat(period.WindSpeedKmh), WindSpeedMph: copyFloat(period.WindSpeedMph), WindGustKmh: copyFloat(period.WindGustKmh), WindGustMph: copyFloat(period.WindGustMph), WindDirection: windDirectionLabel(period.WindDirectionDegrees), ProbabilityOfPrecipitationPercent: copyFloat(period.ProbabilityOfPrecipitationPercent), }) } return out } func (v NarrativeForecastModule) isEmpty() bool { return v.Product == "" && v.IssuedAt.IsZero() && v.UpdatedAt == nil && v.SourceLocation == "" && v.SourceLocationID == "" && len(v.Periods) == 0 }