155 lines
5.7 KiB
Go
155 lines
5.7 KiB
Go
package briefing
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
|
|
)
|
|
|
|
const defaultSPCRiskDigestOutlookType = "categorical"
|
|
const defaultSPCRiskDigestMinimumSeverityRank = 3
|
|
|
|
type SPCConvectiveOutlooksModule struct {
|
|
Checked bool `json:"checked"`
|
|
AsOf string `json:"as_of,omitempty"`
|
|
IssuedAt string `json:"issued_at,omitempty"`
|
|
LocationID string `json:"location_id,omitempty"`
|
|
LocationName string `json:"location_name,omitempty"`
|
|
OutlookCount int `json:"outlook_count"`
|
|
Outlooks []SPCConvectiveOutlookRecord `json:"outlooks,omitempty"`
|
|
RiskDigest []SPCConvectiveOutlookDigest `json:"risk_digest,omitempty"`
|
|
}
|
|
|
|
type SPCConvectiveOutlookRecord struct {
|
|
Day int `json:"day,omitempty"`
|
|
OutlookType string `json:"outlook_type,omitempty"`
|
|
Label string `json:"label,omitempty"`
|
|
LabelText string `json:"label_text,omitempty"`
|
|
PeriodBegins string `json:"period_begins,omitempty"`
|
|
PeriodEnds string `json:"period_ends,omitempty"`
|
|
IssuedAt string `json:"issued_at,omitempty"`
|
|
ContainsLocation bool `json:"contains_location"`
|
|
ImageURL string `json:"image_url,omitempty"`
|
|
}
|
|
|
|
type SPCConvectiveOutlookDigest struct {
|
|
LabelText string `json:"label_text,omitempty"`
|
|
RiskLabel string `json:"risk_label,omitempty"`
|
|
PeriodBegins string `json:"period_begins,omitempty"`
|
|
PeriodEnds string `json:"period_ends,omitempty"`
|
|
}
|
|
|
|
func buildSPCConvectiveOutlooksModule(ctx ModuleContext, _ any) (*module.Output, error) {
|
|
value := SPCConvectiveOutlooksModule{}
|
|
run := ctx.Collected.SPCConvectiveOutlooks
|
|
if run != nil {
|
|
value.Checked = true
|
|
value.AsOf = friendlyOptionalTime(run.AsOf, ctx.Timezone)
|
|
value.IssuedAt = friendlyOptionalTime(run.IssuedAt, ctx.Timezone)
|
|
value.LocationID = run.LocationID
|
|
value.LocationName = run.LocationName
|
|
}
|
|
source, ok := sourceByName(ctx.Collected.SourceProvenance, string(module.SPCConvectiveOutlooks))
|
|
if value.Checked && value.AsOf == "" && ok && !source.FetchedAt.IsZero() {
|
|
value.AsOf = friendlyDateTimeLabel(source.FetchedAt, ctx.Timezone)
|
|
}
|
|
if value.Checked && value.IssuedAt == "" && ok {
|
|
value.IssuedAt = friendlyOptionalTime(source.IssuedAt, ctx.Timezone)
|
|
}
|
|
|
|
value.Outlooks = spcConvectiveOutlookRecords(ctx.Derived.SPCConvectiveOutlooks, ctx.Resolved.ValidPeriod, ctx.Timezone)
|
|
value.RiskDigest = spcConvectiveOutlookRiskDigest(ctx.Derived.SPCConvectiveOutlooks, ctx.Resolved.ValidPeriod, ctx.Timezone, defaultSPCRiskDigestPolicy())
|
|
value.OutlookCount = len(value.Outlooks)
|
|
return &module.Output{ID: module.SPCConvectiveOutlooks, StanzaName: string(module.SPCConvectiveOutlooks), Value: value}, nil
|
|
}
|
|
|
|
type spcRiskDigestPolicy struct {
|
|
OutlookType string
|
|
MinimumSeverityRank int
|
|
}
|
|
|
|
func defaultSPCRiskDigestPolicy() spcRiskDigestPolicy {
|
|
return spcRiskDigestPolicy{
|
|
OutlookType: defaultSPCRiskDigestOutlookType,
|
|
MinimumSeverityRank: defaultSPCRiskDigestMinimumSeverityRank,
|
|
}
|
|
}
|
|
|
|
func spcConvectiveOutlookRecords(outlooks []weatherdata.ConvectiveOutlook, reportPeriod timeutil.Period, timezone string) []SPCConvectiveOutlookRecord {
|
|
records := make([]SPCConvectiveOutlookRecord, 0, len(outlooks))
|
|
for _, outlook := range outlooks {
|
|
outlookPeriod := timeutil.Period{Start: outlook.ValidFrom, End: outlook.ValidTo}
|
|
if !outlookPeriod.IsValid() || !outlookPeriod.Overlaps(reportPeriod) {
|
|
continue
|
|
}
|
|
records = append(records, SPCConvectiveOutlookRecord{
|
|
Day: outlook.Day,
|
|
OutlookType: outlook.OutlookType,
|
|
Label: outlook.Label,
|
|
LabelText: outlook.LabelText,
|
|
PeriodBegins: friendlyPeriodBeginsLabel(outlookPeriod, timezone),
|
|
PeriodEnds: friendlyPeriodEndsLabel(outlookPeriod, timezone),
|
|
IssuedAt: friendlyOptionalTime(outlook.IssuedAt, timezone),
|
|
ContainsLocation: outlook.ContainsLocation,
|
|
ImageURL: outlook.ImageURL,
|
|
})
|
|
}
|
|
return records
|
|
}
|
|
|
|
func spcConvectiveOutlookRiskDigest(outlooks []weatherdata.ConvectiveOutlook, reportPeriod timeutil.Period, timezone string, policy spcRiskDigestPolicy) []SPCConvectiveOutlookDigest {
|
|
records := make([]SPCConvectiveOutlookDigest, 0, len(outlooks))
|
|
for _, outlook := range outlooks {
|
|
if outlook.OutlookType != policy.OutlookType {
|
|
continue
|
|
}
|
|
if outlook.SeverityRank == nil || *outlook.SeverityRank < policy.MinimumSeverityRank {
|
|
continue
|
|
}
|
|
if !outlook.ContainsLocation {
|
|
continue
|
|
}
|
|
outlookPeriod := timeutil.Period{Start: outlook.ValidFrom, End: outlook.ValidTo}
|
|
if !outlookPeriod.IsValid() || !outlookPeriod.Overlaps(reportPeriod) {
|
|
continue
|
|
}
|
|
records = append(records, SPCConvectiveOutlookDigest{
|
|
LabelText: outlook.LabelText,
|
|
RiskLabel: spcRiskDigestLabel(outlook.LabelText),
|
|
PeriodBegins: friendlyMonthDayTimeLabel(outlookPeriod.Start, timezone),
|
|
PeriodEnds: friendlyMonthDayTimeLabel(outlookPeriod.End, timezone),
|
|
})
|
|
}
|
|
return records
|
|
}
|
|
|
|
func spcRiskDigestLabel(labelText string) string {
|
|
label := strings.TrimSpace(labelText)
|
|
if label == "" {
|
|
return ""
|
|
}
|
|
runes := []rune(strings.ToLower(label))
|
|
runes[0] = unicode.ToUpper(runes[0])
|
|
return string(runes)
|
|
}
|
|
|
|
func friendlyOptionalTime(value *time.Time, timezone string) string {
|
|
if value == nil {
|
|
return ""
|
|
}
|
|
return friendlyDateTimeLabel(*value, timezone)
|
|
}
|
|
|
|
func sourceByName(sources []weatherdata.Source, name string) (weatherdata.Source, bool) {
|
|
for _, source := range sources {
|
|
if source.Name == name {
|
|
return source, true
|
|
}
|
|
}
|
|
return weatherdata.Source{}, false
|
|
}
|