Add collected and derived fact contracts

This commit is contained in:
2026-06-09 20:23:53 +00:00
parent 454f47b2b5
commit e9508089ab
10 changed files with 519 additions and 65 deletions

View File

@@ -3,6 +3,7 @@ package briefing
import (
"fmt"
"gitea.maximumdirect.net/eric/weatherreporter/internal/facts"
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
@@ -26,7 +27,7 @@ type Storm struct {
WeatherStory *WeatherStoryContext `json:"weatherStory,omitempty"`
}
func BuildStorm(ctx BuildContext) (Package, error) {
func BuildStorm(ctx BuildContext, derived facts.DerivedFacts) (Package, error) {
if ctx.Resolved.Definition.ID != report.Storm {
return Package{}, fmt.Errorf("storm briefing requires a storm report definition")
}
@@ -34,12 +35,14 @@ func BuildStorm(ctx BuildContext) (Package, error) {
return Package{}, fmt.Errorf("forecast bundle is required")
}
period := ctx.Resolved.ValidPeriod
hourly := forecast.SelectHourlyPeriods(ctx.Bundle.Hourly, period)
narrative := forecast.SelectNarrativePeriods(ctx.Bundle, period)
daily := forecast.SelectHourlyPeriods(ctx.Bundle.Daily, period)
alerts := forecast.AlertOverlaps(ctx.Bundle.Alerts, period)
summary := forecast.SummarizeDaypart("storm window", period, hourly)
summary.AlertOverlaps = alerts
hourly := derived.ValidPeriodHourlyPeriods
narrative := derived.ValidPeriodNarrativePeriods
daily := derived.ValidPeriodDailyPeriods
alerts := derived.AlertOverlaps
if derived.StormWindowSummary == nil {
return Package{}, fmt.Errorf("storm window summary is required")
}
summary := *derived.StormWindowSummary
storm := &Storm{
TimingWindow: period,

View File

@@ -6,6 +6,8 @@ import (
"testing"
"time"
"gitea.maximumdirect.net/eric/weatherreporter/internal/facts"
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
)
@@ -62,7 +64,7 @@ func TestStormBriefingWithActiveAlert(t *testing.T) {
Sources: []weatherdata.Source{{Name: "hourly", FetchedAt: time.Now()}},
}
pkg, err := BuildStorm(BuildContext{Resolved: resolved, Bundle: bundle, Units: "us", Timezone: "America/Chicago"})
pkg, err := BuildStorm(BuildContext{Resolved: resolved, Bundle: bundle, Units: "us", Timezone: "America/Chicago"}, stormDerivedFacts(t, resolved, bundle))
if err != nil {
t.Fatalf("BuildStorm() error = %v", err)
}
@@ -120,7 +122,7 @@ func TestStormBriefingWithDiscussionButNoAlert(t *testing.T) {
Sources: []weatherdata.Source{{Name: "hourly", FetchedAt: time.Now()}},
}
pkg, err := BuildStorm(BuildContext{Resolved: resolved, Bundle: bundle, Units: "us", Timezone: "America/Chicago"})
pkg, err := BuildStorm(BuildContext{Resolved: resolved, Bundle: bundle, Units: "us", Timezone: "America/Chicago"}, stormDerivedFacts(t, resolved, bundle))
if err != nil {
t.Fatalf("BuildStorm() error = %v", err)
}
@@ -155,7 +157,7 @@ func TestStormBriefingQuietWindow(t *testing.T) {
Sources: []weatherdata.Source{{Name: "hourly", FetchedAt: time.Now()}},
}
pkg, err := BuildStorm(BuildContext{Resolved: resolved, Bundle: bundle, Units: "us", Timezone: "America/Chicago"})
pkg, err := BuildStorm(BuildContext{Resolved: resolved, Bundle: bundle, Units: "us", Timezone: "America/Chicago"}, stormDerivedFacts(t, resolved, bundle))
if err != nil {
t.Fatalf("BuildStorm() error = %v", err)
}
@@ -167,3 +169,17 @@ func TestStormBriefingQuietWindow(t *testing.T) {
t.Fatalf("WhatToWatchNext = %#v, want watch fallback", pkg.Storm.WhatToWatchNext)
}
}
func stormDerivedFacts(t *testing.T, resolved report.Resolved, bundle *weatherdata.Bundle) facts.DerivedFacts {
t.Helper()
derived, err := facts.BuildDerived(facts.BuildDerivedRequest{
Resolved: resolved,
Timezone: "America/Chicago",
Dayparts: []forecast.DaypartDefinition{{Name: "morning", Start: "06:00", End: "12:00"}},
Collected: facts.BuildCollected(bundle),
})
if err != nil {
t.Fatalf("build derived facts: %v", err)
}
return derived
}