package briefing import ( "fmt" "gitea.maximumdirect.net/eric/weatherreporter/internal/module" "gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil" "gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata" ) const defaultSPCConvectiveDiscussionMinimumSeverityRank = defaultSPCRiskDigestMinimumSeverityRank const spcCategoricalOutlookType = defaultSPCRiskDigestOutlookType type SPCConvectiveDiscussionModule struct { IncludedBecause string `json:"included_because"` Discussions []SPCConvectiveDiscussionRecord `json:"discussions"` } type SPCConvectiveDiscussionRecord struct { Day int `json:"day,omitempty"` PeriodBegins string `json:"period_begins,omitempty"` PeriodEnds string `json:"period_ends,omitempty"` Headline string `json:"headline,omitempty"` Summary string `json:"summary,omitempty"` Discussion string `json:"discussion,omitempty"` UpdatedAt string `json:"updated_at,omitempty"` } func buildSPCConvectiveDiscussionModule(ctx ModuleContext, _ any) (*module.Output, error) { qualifyingPeriods := spcConvectiveDiscussionQualifyingPeriods(ctx.Derived.SPCConvectiveOutlooks, ctx.Resolved.ValidPeriod) if len(qualifyingPeriods) == 0 { return nil, nil } records := spcConvectiveDiscussionRecords(qualifyingPeriods, ctx.Derived.SPCConvectiveDiscussions, ctx.Timezone) if len(records) == 0 { return nil, nil } value := SPCConvectiveDiscussionModule{ IncludedBecause: fmt.Sprintf("%s severity_rank >= %d", spcCategoricalOutlookType, defaultSPCConvectiveDiscussionMinimumSeverityRank), Discussions: records, } return &module.Output{ID: module.SPCConvectiveDiscussion, StanzaName: string(module.SPCConvectiveDiscussion), Value: value}, nil } func spcConvectiveDiscussionQualifyingPeriods(outlooks []weatherdata.ConvectiveOutlook, reportPeriod timeutil.Period) map[int]timeutil.Period { periods := map[int]timeutil.Period{} for _, outlook := range outlooks { if outlook.OutlookType != spcCategoricalOutlookType { continue } if outlook.SeverityRank == nil || *outlook.SeverityRank < defaultSPCConvectiveDiscussionMinimumSeverityRank { continue } outlookPeriod := timeutil.Period{Start: outlook.ValidFrom, End: outlook.ValidTo} if !outlookPeriod.IsValid() || !outlookPeriod.Overlaps(reportPeriod) { continue } if existing, ok := periods[outlook.Day]; ok { if outlookPeriod.Start.Before(existing.Start) { existing.Start = outlookPeriod.Start } if outlookPeriod.End.After(existing.End) { existing.End = outlookPeriod.End } periods[outlook.Day] = existing continue } periods[outlook.Day] = outlookPeriod } return periods } func spcConvectiveDiscussionRecords(qualifyingPeriods map[int]timeutil.Period, discussions []weatherdata.ConvectiveOutlookDiscussion, timezone string) []SPCConvectiveDiscussionRecord { records := make([]SPCConvectiveDiscussionRecord, 0, len(discussions)) for _, discussion := range discussions { period, ok := qualifyingPeriods[discussion.Day] if !ok { continue } if discussion.Discussion == "" { continue } records = append(records, SPCConvectiveDiscussionRecord{ Day: discussion.Day, PeriodBegins: friendlyPeriodBeginsLabel(period, timezone), PeriodEnds: friendlyPeriodEndsLabel(period, timezone), Headline: discussion.Headline, Summary: discussion.Summary, Discussion: discussion.Discussion, UpdatedAt: friendlyOptionalTime(discussion.UpdatedAt, timezone), }) } return records }