Add convective outlook application filtering

This commit is contained in:
2026-06-11 15:27:03 +00:00
parent 27d6183b24
commit 6e0b71f6f4
2 changed files with 352 additions and 0 deletions

View File

@@ -4,6 +4,8 @@ package app
import (
"context"
"strings"
"time"
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
)
@@ -17,9 +19,18 @@ type Repository interface {
LatestWeatherStoryRun(ctx context.Context) (*model.WeatherStoryRun, error)
LatestWeatherStory(ctx context.Context) (*model.WeatherStory, error)
LatestAlertRun(ctx context.Context) (*model.WeatherAlertRun, error)
LatestConvectiveOutlookRun(ctx context.Context) (*model.WeatherOutlookRun, error)
CurrentConditions(ctx context.Context, observationWindowMinutes int) (*CurrentConditions, error)
}
// OutlookFilter selects outlook entries from the latest convective outlook run.
type OutlookFilter struct {
Day *int
OutlookType string
ContainsLocation *bool
ActiveAt *time.Time
}
// Service provides weather read use-cases.
type Service struct {
repo Repository
@@ -57,6 +68,93 @@ func (s *Service) LatestAlertRun(ctx context.Context) (*model.WeatherAlertRun, e
return s.repo.LatestAlertRun(ctx)
}
func (s *Service) LatestConvectiveOutlook(ctx context.Context, filter OutlookFilter) (*model.WeatherOutlookRun, error) {
run, err := s.repo.LatestConvectiveOutlookRun(ctx)
if err != nil {
return nil, err
}
if run == nil {
return nil, nil
}
out := cloneOutlookRun(run)
outlooks := out.Outlooks[:0]
for _, outlook := range out.Outlooks {
if matchesOutlookFilter(outlook, filter) {
outlooks = append(outlooks, outlook)
}
}
out.Outlooks = outlooks
return out, nil
}
func (s *Service) CurrentConditions(ctx context.Context) (*CurrentConditions, error) {
return s.repo.CurrentConditions(ctx, ObservationWindowMinutesDefault)
}
func matchesOutlookFilter(outlook model.WeatherOutlook, filter OutlookFilter) bool {
if filter.Day != nil && outlook.Day != *filter.Day {
return false
}
if filter.OutlookType != "" && outlook.OutlookType != normalizeOutlookType(filter.OutlookType) {
return false
}
if filter.ContainsLocation != nil && outlook.ContainsLocation != *filter.ContainsLocation {
return false
}
if filter.ActiveAt != nil && (filter.ActiveAt.Before(outlook.ValidFrom) || !filter.ActiveAt.Before(outlook.ValidTo)) {
return false
}
return true
}
func normalizeOutlookType(value string) string {
return strings.ToLower(strings.TrimSpace(value))
}
func cloneOutlookRun(run *model.WeatherOutlookRun) *model.WeatherOutlookRun {
out := *run
out.Latitude = copyFloat64(run.Latitude)
out.Longitude = copyFloat64(run.Longitude)
out.IssuedAt = copyTime(run.IssuedAt)
if run.Outlooks != nil {
out.Outlooks = make([]model.WeatherOutlook, len(run.Outlooks))
for i := range run.Outlooks {
out.Outlooks[i] = cloneOutlook(run.Outlooks[i])
}
}
return &out
}
func cloneOutlook(outlook model.WeatherOutlook) model.WeatherOutlook {
out := outlook
out.SeverityRank = copyInt(outlook.SeverityRank)
if outlook.Geometry != nil {
out.Geometry = append([]byte(nil), outlook.Geometry...)
}
return out
}
func copyFloat64(value *float64) *float64 {
if value == nil {
return nil
}
out := *value
return &out
}
func copyInt(value *int) *int {
if value == nil {
return nil
}
out := *value
return &out
}
func copyTime(value *time.Time) *time.Time {
if value == nil {
return nil
}
out := *value
return &out
}