Split weather data types from forecast derivation

This commit is contained in:
2026-06-09 20:16:47 +00:00
parent d8b417458b
commit 454f47b2b5
22 changed files with 234 additions and 223 deletions

View File

@@ -9,18 +9,19 @@ import (
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
)
type Daily struct {
BottomLine BottomLine `json:"bottomLine"`
Dayparts []forecast.DaypartSummary `json:"dayparts"`
RelevantAlerts []forecast.AlertOverlap `json:"relevantAlerts,omitempty"`
OutdoorWindows OutdoorWindows `json:"outdoorWindows"`
Planning *TomorrowPlanning `json:"planning,omitempty"`
NarrativePeriods []forecast.ForecastPeriod `json:"narrativePeriods,omitempty"`
Discussion DiscussionContext `json:"discussion,omitempty"`
WeatherStory *WeatherStoryContext `json:"weatherStory,omitempty"`
ForecastSummaryDate string `json:"forecastSummaryDate"`
BottomLine BottomLine `json:"bottomLine"`
Dayparts []forecast.DaypartSummary `json:"dayparts"`
RelevantAlerts []forecast.AlertOverlap `json:"relevantAlerts,omitempty"`
OutdoorWindows OutdoorWindows `json:"outdoorWindows"`
Planning *TomorrowPlanning `json:"planning,omitempty"`
NarrativePeriods []weatherdata.ForecastPeriod `json:"narrativePeriods,omitempty"`
Discussion DiscussionContext `json:"discussion,omitempty"`
WeatherStory *WeatherStoryContext `json:"weatherStory,omitempty"`
ForecastSummaryDate string `json:"forecastSummaryDate"`
}
type BottomLine struct {
@@ -250,7 +251,7 @@ func daypartNamed(dayparts []forecast.DaypartSummary, name string) *forecast.Day
return nil
}
func buildDiscussion(discussion *forecast.Discussion) DiscussionContext {
func buildDiscussion(discussion *weatherdata.Discussion) DiscussionContext {
if discussion == nil {
return DiscussionContext{}
}
@@ -267,7 +268,7 @@ func buildDiscussion(discussion *forecast.Discussion) DiscussionContext {
return ctx
}
func buildWeatherStory(bundle *forecast.Bundle) *WeatherStoryContext {
func buildWeatherStory(bundle *weatherdata.Bundle) *WeatherStoryContext {
if bundle == nil || bundle.WeatherStory == nil {
return nil
}

View File

@@ -11,6 +11,7 @@ import (
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
)
func TestDailyBriefingFromRepresentativeFixture(t *testing.T) {
@@ -20,7 +21,7 @@ func TestDailyBriefingFromRepresentativeFixture(t *testing.T) {
currentFeelsLike := 76.1
currentHumidity := 56.0
currentWind := 10.7
bundle.Current = &forecast.Current{
bundle.Current = &weatherdata.Current{
ConditionText: "Partly cloudy",
IsDay: &currentIsDay,
TemperatureF: &currentTemp,
@@ -29,7 +30,7 @@ func TestDailyBriefingFromRepresentativeFixture(t *testing.T) {
WindSpeedMph: &currentWind,
}
bundle.Sources[0].DataSHA256 = "abc123"
bundle.Warnings = []forecast.SourceWarning{{Source: "daily", Code: "missing_source", Severity: "warning"}}
bundle.Warnings = []weatherdata.SourceWarning{{Source: "daily", Code: "missing_source", Severity: "warning"}}
location := mustLocation(t)
resolved := mustResolveDaily(t, location)
summary, err := forecast.BuildDailySummary(bundle, resolved.ValidPeriod.Start, location, defaultDayparts())
@@ -112,17 +113,17 @@ func TestDailyBriefingFromRepresentativeFixture(t *testing.T) {
func TestDailyBriefingQuietWeather(t *testing.T) {
location := mustLocation(t)
resolved := mustResolveDaily(t, location)
bundle := &forecast.Bundle{
Hourly: &forecast.ForecastRun{Periods: []forecast.ForecastPeriod{
bundle := &weatherdata.Bundle{
Hourly: &weatherdata.ForecastRun{Periods: []weatherdata.ForecastPeriod{
quietHour("2026-05-29T09:00:00-05:00", "2026-05-29T10:00:00-05:00", 72),
}},
Alerts: &forecast.AlertRun{},
Sources: []forecast.Source{
Alerts: &weatherdata.AlertRun{},
Sources: []weatherdata.Source{
{Name: "hourly", FetchedAt: time.Now()},
{Name: "alerts", Endpoint: "/alerts/active", FetchedAt: time.Now()},
{Name: "current", Endpoint: "/conditions/current", FetchedAt: time.Now(), Missing: true},
},
Warnings: []forecast.SourceWarning{{Source: "current", Code: "missing_source", Severity: "warning"}},
Warnings: []weatherdata.SourceWarning{{Source: "current", Code: "missing_source", Severity: "warning"}},
}
summary, err := forecast.BuildDailySummary(bundle, resolved.ValidPeriod.Start, location, defaultDayparts())
if err != nil {
@@ -163,7 +164,7 @@ func TestDailyBriefingAlertExclusion(t *testing.T) {
location := mustLocation(t)
resolved := mustResolveDaily(t, location)
bundle := loadBundleFixture(t)
bundle.Alerts = &forecast.AlertRun{Alerts: []json.RawMessage{
bundle.Alerts = &weatherdata.AlertRun{Alerts: []json.RawMessage{
json.RawMessage(`{"event":"Future Watch","effective":"2026-06-01T00:00:00-05:00","expires":"2026-06-01T06:00:00-05:00"}`),
}}
summary, err := forecast.BuildDailySummary(bundle, resolved.ValidPeriod.Start, location, defaultDayparts())
@@ -265,13 +266,13 @@ func TestSaveBriefingPackage(t *testing.T) {
}
}
func loadBundleFixture(t *testing.T) *forecast.Bundle {
func loadBundleFixture(t *testing.T) *weatherdata.Bundle {
t.Helper()
data, err := os.ReadFile(filepath.Join("..", "forecast", "testdata", "daily_bundle.json"))
if err != nil {
t.Fatalf("read bundle fixture: %v", err)
}
var bundle forecast.Bundle
var bundle weatherdata.Bundle
if err := json.Unmarshal(data, &bundle); err != nil {
t.Fatalf("decode bundle fixture: %v", err)
}
@@ -300,8 +301,8 @@ func defaultDayparts() []forecast.DaypartDefinition {
}
}
func quietHour(start string, end string, temperature float64) forecast.ForecastPeriod {
return forecast.ForecastPeriod{
func quietHour(start string, end string, temperature float64) weatherdata.ForecastPeriod {
return weatherdata.ForecastPeriod{
StartTime: mustParse(start),
EndTime: mustParse(end),
TextDescription: "Clear",

View File

@@ -6,9 +6,9 @@ import (
"time"
"gitea.maximumdirect.net/eric/weatherreporter/internal/fileutil"
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
)
const SchemaVersion = "weatherreporter.briefing.v1"
@@ -23,21 +23,21 @@ type Package struct {
}
type Metadata struct {
SchemaVersion string `json:"schemaVersion"`
RunID string `json:"runId"`
ReportID report.ID `json:"reportId"`
Variant string `json:"variant,omitempty"`
PromptID string `json:"promptId"`
GeneratedAt time.Time `json:"generatedAt"`
Units string `json:"units"`
Timezone string `json:"timezone"`
ValidPeriod timeutil.Period `json:"validPeriod"`
Location *LocationContext `json:"location,omitempty"`
SourceLocationID string `json:"sourceLocationId,omitempty"`
SourceLocation string `json:"sourceLocation,omitempty"`
Sources []SourceMetadata `json:"sources,omitempty"`
SourceWarnings []forecast.SourceWarning `json:"sourceWarnings,omitempty"`
Alerts *AlertStatus `json:"alerts,omitempty"`
SchemaVersion string `json:"schemaVersion"`
RunID string `json:"runId"`
ReportID report.ID `json:"reportId"`
Variant string `json:"variant,omitempty"`
PromptID string `json:"promptId"`
GeneratedAt time.Time `json:"generatedAt"`
Units string `json:"units"`
Timezone string `json:"timezone"`
ValidPeriod timeutil.Period `json:"validPeriod"`
Location *LocationContext `json:"location,omitempty"`
SourceLocationID string `json:"sourceLocationId,omitempty"`
SourceLocation string `json:"sourceLocation,omitempty"`
Sources []SourceMetadata `json:"sources,omitempty"`
SourceWarnings []weatherdata.SourceWarning `json:"sourceWarnings,omitempty"`
Alerts *AlertStatus `json:"alerts,omitempty"`
}
type LocationContext struct {
@@ -63,14 +63,14 @@ type CurrentConditionsContext struct {
}
type SourceMetadata struct {
Name string `json:"name"`
Endpoint string `json:"endpoint,omitempty"`
FetchedAt time.Time `json:"fetchedAt"`
IssuedAt *time.Time `json:"issuedAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
DataSHA256 string `json:"dataSha256,omitempty"`
Missing bool `json:"missing,omitempty"`
Warnings []forecast.SourceWarning `json:"warnings,omitempty"`
Name string `json:"name"`
Endpoint string `json:"endpoint,omitempty"`
FetchedAt time.Time `json:"fetchedAt"`
IssuedAt *time.Time `json:"issuedAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
DataSHA256 string `json:"dataSha256,omitempty"`
Missing bool `json:"missing,omitempty"`
Warnings []weatherdata.SourceWarning `json:"warnings,omitempty"`
}
type AlertStatus struct {
@@ -82,7 +82,7 @@ type AlertStatus struct {
type BuildContext struct {
Resolved report.Resolved
Bundle *forecast.Bundle
Bundle *weatherdata.Bundle
Units string
Timezone string
Location *LocationContext
@@ -125,7 +125,7 @@ func copyLocation(location *LocationContext) *LocationContext {
return &copied
}
func currentConditions(bundle *forecast.Bundle) *CurrentConditionsContext {
func currentConditions(bundle *weatherdata.Bundle) *CurrentConditionsContext {
if bundle == nil || bundle.Current == nil {
return nil
}
@@ -184,11 +184,11 @@ func Save(path string, pkg Package) error {
return nil
}
func sourceLocation(bundle *forecast.Bundle) (string, string) {
func sourceLocation(bundle *weatherdata.Bundle) (string, string) {
if bundle == nil {
return "", ""
}
for _, run := range []*forecast.ForecastRun{bundle.Hourly, bundle.Narrative, bundle.Daily} {
for _, run := range []*weatherdata.ForecastRun{bundle.Hourly, bundle.Narrative, bundle.Daily} {
if run == nil {
continue
}
@@ -199,7 +199,7 @@ func sourceLocation(bundle *forecast.Bundle) (string, string) {
return "", ""
}
func sourceMetadata(bundle *forecast.Bundle) []SourceMetadata {
func sourceMetadata(bundle *weatherdata.Bundle) []SourceMetadata {
if bundle == nil {
return nil
}
@@ -219,14 +219,14 @@ func sourceMetadata(bundle *forecast.Bundle) []SourceMetadata {
return out
}
func sourceWarnings(bundle *forecast.Bundle) []forecast.SourceWarning {
func sourceWarnings(bundle *weatherdata.Bundle) []weatherdata.SourceWarning {
if bundle == nil {
return nil
}
return bundle.Warnings
}
func alertStatus(bundle *forecast.Bundle) *AlertStatus {
func alertStatus(bundle *weatherdata.Bundle) *AlertStatus {
if bundle == nil {
return nil
}

View File

@@ -6,23 +6,24 @@ import (
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
)
type Storm struct {
TimingWindow timeutil.Period `json:"timingWindow"`
EventHeadlines []string `json:"eventHeadlines,omitempty"`
Hazards []string `json:"hazards,omitempty"`
MostLikelyScenario []string `json:"mostLikelyScenario,omitempty"`
ReasonableWorstCase []string `json:"reasonableWorstCase,omitempty"`
ConfidenceInputs []string `json:"confidenceInputs,omitempty"`
WhatToWatchNext []string `json:"whatToWatchNext,omitempty"`
RelevantAlerts []forecast.AlertOverlap `json:"relevantAlerts,omitempty"`
HourlyPeriods []forecast.ForecastPeriod `json:"hourlyPeriods,omitempty"`
DailyPeriods []forecast.ForecastPeriod `json:"dailyPeriods,omitempty"`
NarrativePeriods []forecast.ForecastPeriod `json:"narrativePeriods,omitempty"`
WindowSummary forecast.DaypartSummary `json:"windowSummary"`
Discussion DiscussionContext `json:"discussion,omitempty"`
WeatherStory *WeatherStoryContext `json:"weatherStory,omitempty"`
TimingWindow timeutil.Period `json:"timingWindow"`
EventHeadlines []string `json:"eventHeadlines,omitempty"`
Hazards []string `json:"hazards,omitempty"`
MostLikelyScenario []string `json:"mostLikelyScenario,omitempty"`
ReasonableWorstCase []string `json:"reasonableWorstCase,omitempty"`
ConfidenceInputs []string `json:"confidenceInputs,omitempty"`
WhatToWatchNext []string `json:"whatToWatchNext,omitempty"`
RelevantAlerts []forecast.AlertOverlap `json:"relevantAlerts,omitempty"`
HourlyPeriods []weatherdata.ForecastPeriod `json:"hourlyPeriods,omitempty"`
DailyPeriods []weatherdata.ForecastPeriod `json:"dailyPeriods,omitempty"`
NarrativePeriods []weatherdata.ForecastPeriod `json:"narrativePeriods,omitempty"`
WindowSummary forecast.DaypartSummary `json:"windowSummary"`
Discussion DiscussionContext `json:"discussion,omitempty"`
WeatherStory *WeatherStoryContext `json:"weatherStory,omitempty"`
}
func BuildStorm(ctx BuildContext) (Package, error) {
@@ -102,7 +103,7 @@ func stormHazards(alerts []forecast.AlertOverlap, summary forecast.DaypartSummar
return out
}
func mostLikelyStormScenario(hourly []forecast.ForecastPeriod, narrative []forecast.ForecastPeriod, summary forecast.DaypartSummary) []string {
func mostLikelyStormScenario(hourly []weatherdata.ForecastPeriod, narrative []weatherdata.ForecastPeriod, summary forecast.DaypartSummary) []string {
var items []string
if summary.DominantCondition != "" {
items = append(items, "Dominant hourly condition: "+summary.DominantCondition+".")
@@ -151,7 +152,7 @@ func reasonableWorstCase(alerts []forecast.AlertOverlap, summary forecast.Daypar
return items
}
func stormConfidenceInputs(bundle *forecast.Bundle) []string {
func stormConfidenceInputs(bundle *weatherdata.Bundle) []string {
var items []string
if bundle == nil {
return []string{"No source bundle was available for confidence context."}
@@ -180,7 +181,7 @@ func stormConfidenceInputs(bundle *forecast.Bundle) []string {
return items
}
func stormWatchItems(alerts []forecast.AlertOverlap, summary forecast.DaypartSummary, bundle *forecast.Bundle) []string {
func stormWatchItems(alerts []forecast.AlertOverlap, summary forecast.DaypartSummary, bundle *weatherdata.Bundle) []string {
var items []string
if len(alerts) > 0 {
items = append(items, "Watch for alert extensions, cancellations, or upgrades.")

View File

@@ -6,8 +6,8 @@ import (
"testing"
"time"
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
)
func TestStormBriefingWithActiveAlert(t *testing.T) {
@@ -23,34 +23,34 @@ func TestStormBriefingWithActiveAlert(t *testing.T) {
}
precip := 80.0
gust := 42.0
bundle := &forecast.Bundle{
Hourly: &forecast.ForecastRun{Periods: []forecast.ForecastPeriod{{
bundle := &weatherdata.Bundle{
Hourly: &weatherdata.ForecastRun{Periods: []weatherdata.ForecastPeriod{{
StartTime: mustParse("2026-05-29T07:00:00-05:00"),
EndTime: mustParse("2026-05-29T08:00:00-05:00"),
TextDescription: "Severe thunderstorms and gusty wind",
ProbabilityOfPrecipitationPercent: &precip,
WindGustMph: &gust,
}}},
Daily: &forecast.ForecastRun{Periods: []forecast.ForecastPeriod{{
Daily: &weatherdata.ForecastRun{Periods: []weatherdata.ForecastPeriod{{
StartTime: mustParse("2026-05-29T06:00:00-05:00"),
EndTime: mustParse("2026-05-29T18:00:00-05:00"),
TextDescription: "Storms likely.",
}}},
Narrative: &forecast.ForecastRun{Periods: []forecast.ForecastPeriod{{
Narrative: &weatherdata.ForecastRun{Periods: []weatherdata.ForecastPeriod{{
StartTime: mustParse("2026-05-29T06:00:00-05:00"),
EndTime: mustParse("2026-05-29T18:00:00-05:00"),
TextDescription: "Damaging wind possible in stronger storms.",
}}},
Alerts: &forecast.AlertRun{Alerts: []json.RawMessage{
Alerts: &weatherdata.AlertRun{Alerts: []json.RawMessage{
json.RawMessage(`{"event":"Severe Thunderstorm Warning","headline":"Severe storms near Testville","severity":"Severe","effective":"2026-05-29T06:30:00-05:00","expires":"2026-05-29T08:30:00-05:00"}`),
}},
Discussion: &forecast.Discussion{
Discussion: &weatherdata.Discussion{
Product: "discussion",
KeyMessages: []string{"Storms may intensify quickly."},
ShortTerm: &forecast.DiscussionSection{Text: "Short-term storm coverage peaks this morning."},
LongTerm: &forecast.DiscussionSection{Text: "Long-term pattern stays unsettled after the event."},
ShortTerm: &weatherdata.DiscussionSection{Text: "Short-term storm coverage peaks this morning."},
LongTerm: &weatherdata.DiscussionSection{Text: "Long-term pattern stays unsettled after the event."},
},
WeatherStory: &forecast.WeatherStory{
WeatherStory: &weatherdata.WeatherStory{
OfficeID: "LSX",
StartTime: mustParse("2026-05-29T06:00:00Z"),
EndTime: mustParse("2026-05-29T18:00:00Z"),
@@ -59,7 +59,7 @@ func TestStormBriefingWithActiveAlert(t *testing.T) {
AltText: "Weather story graphic showing storm risk.",
Order: 1,
},
Sources: []forecast.Source{{Name: "hourly", FetchedAt: time.Now()}},
Sources: []weatherdata.Source{{Name: "hourly", FetchedAt: time.Now()}},
}
pkg, err := BuildStorm(BuildContext{Resolved: resolved, Bundle: bundle, Units: "us", Timezone: "America/Chicago"})
@@ -113,11 +113,11 @@ func TestStormBriefingWithDiscussionButNoAlert(t *testing.T) {
if err != nil {
t.Fatalf("resolve storm: %v", err)
}
bundle := &forecast.Bundle{
Hourly: &forecast.ForecastRun{Periods: []forecast.ForecastPeriod{{StartTime: mustParse("2026-05-29T07:00:00-05:00"), EndTime: mustParse("2026-05-29T08:00:00-05:00"), TextDescription: "Showers"}}},
Alerts: &forecast.AlertRun{},
Discussion: &forecast.Discussion{Product: "discussion", KeyMessages: []string{"Confidence is moderate."}},
Sources: []forecast.Source{{Name: "hourly", FetchedAt: time.Now()}},
bundle := &weatherdata.Bundle{
Hourly: &weatherdata.ForecastRun{Periods: []weatherdata.ForecastPeriod{{StartTime: mustParse("2026-05-29T07:00:00-05:00"), EndTime: mustParse("2026-05-29T08:00:00-05:00"), TextDescription: "Showers"}}},
Alerts: &weatherdata.AlertRun{},
Discussion: &weatherdata.Discussion{Product: "discussion", KeyMessages: []string{"Confidence is moderate."}},
Sources: []weatherdata.Source{{Name: "hourly", FetchedAt: time.Now()}},
}
pkg, err := BuildStorm(BuildContext{Resolved: resolved, Bundle: bundle, Units: "us", Timezone: "America/Chicago"})
@@ -150,9 +150,9 @@ func TestStormBriefingQuietWindow(t *testing.T) {
if err != nil {
t.Fatalf("resolve storm: %v", err)
}
bundle := &forecast.Bundle{
Hourly: &forecast.ForecastRun{Periods: []forecast.ForecastPeriod{{StartTime: mustParse("2026-05-29T07:00:00-05:00"), EndTime: mustParse("2026-05-29T08:00:00-05:00"), TextDescription: "Clear"}}},
Sources: []forecast.Source{{Name: "hourly", FetchedAt: time.Now()}},
bundle := &weatherdata.Bundle{
Hourly: &weatherdata.ForecastRun{Periods: []weatherdata.ForecastPeriod{{StartTime: mustParse("2026-05-29T07:00:00-05:00"), EndTime: mustParse("2026-05-29T08:00:00-05:00"), TextDescription: "Clear"}}},
Sources: []weatherdata.Source{{Name: "hourly", FetchedAt: time.Now()}},
}
pkg, err := BuildStorm(BuildContext{Resolved: resolved, Bundle: bundle, Units: "us", Timezone: "America/Chicago"})

View File

@@ -7,6 +7,7 @@ import (
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
)
func TestThreeDayBriefingBuildsOutlookDays(t *testing.T) {
@@ -43,11 +44,11 @@ func TestThreeDayBriefingBuildsOutlookDays(t *testing.T) {
},
},
AlertOverlaps: []forecast.AlertOverlap{{Event: "Flood Watch"}},
Discussion: &forecast.Discussion{
Discussion: &weatherdata.Discussion{
Product: "discussion",
KeyMessages: []string{"Unsettled stretch."},
ShortTerm: &forecast.DiscussionSection{Text: "Short-term rain chances remain focused today."},
LongTerm: &forecast.DiscussionSection{Text: "Long-term warmth builds into the weekend."},
ShortTerm: &weatherdata.DiscussionSection{Text: "Short-term rain chances remain focused today."},
LongTerm: &weatherdata.DiscussionSection{Text: "Long-term warmth builds into the weekend."},
},
},
{

View File

@@ -6,6 +6,7 @@ import (
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
)
type Weekend struct {
@@ -45,7 +46,7 @@ func BuildWeekend(ctx BuildContext, summaries []forecast.DailySummary) (Package,
return pkg, nil
}
func buildWeekendPlanning(days []OutlookDay, discussion DiscussionContext, bundle *forecast.Bundle) WeekendPlanning {
func buildWeekendPlanning(days []OutlookDay, discussion DiscussionContext, bundle *weatherdata.Bundle) WeekendPlanning {
planning := WeekendPlanning{}
for _, day := range days {
if day.OutdoorWindows.Best != nil {

View File

@@ -7,6 +7,7 @@ import (
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
)
func TestWeekendBriefingBuildsPlanningInputs(t *testing.T) {
@@ -44,7 +45,7 @@ func TestWeekendBriefingBuildsPlanningInputs(t *testing.T) {
Time: mustParse("2026-05-30T16:00:00-05:00"),
},
Indicators: forecast.Indicators{Wind: true},
HourlyPeriods: []forecast.ForecastPeriod{
HourlyPeriods: []weatherdata.ForecastPeriod{
{
StartTime: mustParse("2026-05-30T15:00:00-05:00"),
EndTime: mustParse("2026-05-30T16:00:00-05:00"),
@@ -54,11 +55,11 @@ func TestWeekendBriefingBuildsPlanningInputs(t *testing.T) {
},
},
AlertOverlaps: []forecast.AlertOverlap{{Event: "Flood Watch"}},
Discussion: &forecast.Discussion{
Discussion: &weatherdata.Discussion{
Product: "discussion",
KeyMessages: []string{"Timing may shift."},
ShortTerm: &forecast.DiscussionSection{Text: "Short-term showers exit before the weekend."},
LongTerm: &forecast.DiscussionSection{Text: "Long-term weekend rain timing remains uncertain."},
ShortTerm: &weatherdata.DiscussionSection{Text: "Short-term showers exit before the weekend."},
LongTerm: &weatherdata.DiscussionSection{Text: "Long-term weekend rain timing remains uncertain."},
},
},
}