Add a current conditions block to the briefing/data-package
This commit is contained in:
@@ -22,6 +22,8 @@ Outputs:
|
|||||||
|
|
||||||
- `briefing.Package` with common metadata and one report-specific content
|
- `briefing.Package` with common metadata and one report-specific content
|
||||||
object for Daily, 3-Day, Weekend, or Storm Report
|
object for Daily, 3-Day, Weekend, or Storm Report
|
||||||
|
- optional `currentConditions` prompt context from normalized
|
||||||
|
`/conditions/current` data when available
|
||||||
- optional JSON file written by `briefing.Save`
|
- optional JSON file written by `briefing.Save`
|
||||||
|
|
||||||
## Boundaries
|
## Boundaries
|
||||||
@@ -36,6 +38,8 @@ The package receives configured units and timezone from the app layer. Daypart
|
|||||||
configuration is consumed by `internal/forecast` before briefing builders run.
|
configuration is consumed by `internal/forecast` before briefing builders run.
|
||||||
Configured `location` values are prompt context only; Weather API
|
Configured `location` values are prompt context only; Weather API
|
||||||
`sourceLocationId` and `sourceLocation` remain source provenance.
|
`sourceLocationId` and `sourceLocation` remain source provenance.
|
||||||
|
Current conditions are copied from the normalized `/conditions/current` bundle
|
||||||
|
source only; observation station and timestamp fields remain provenance.
|
||||||
|
|
||||||
## External Adapters Used
|
## External Adapters Used
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ Outputs:
|
|||||||
|
|
||||||
- `promptinput.Package` containing schema version, RunID, report metadata,
|
- `promptinput.Package` containing schema version, RunID, report metadata,
|
||||||
briefing content, Recent Changes, and source warnings. Briefing content
|
briefing content, Recent Changes, and source warnings. Briefing content
|
||||||
includes configured location context, discussion key messages, and
|
includes configured location context, current conditions when available,
|
||||||
short/long-term AFD narratives when the Weather API provides them.
|
discussion key messages, and short/long-term AFD narratives when the Weather
|
||||||
|
API provides them.
|
||||||
- optional JSON file written by `promptinput.Save`
|
- optional JSON file written by `promptinput.Save`
|
||||||
|
|
||||||
## Boundaries
|
## Boundaries
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ This document describes Weather API ingestion into `forecast.Bundle`.
|
|||||||
|
|
||||||
`internal/adapters/weatherapi` fetches normalized weather data from one
|
`internal/adapters/weatherapi` fetches normalized weather data from one
|
||||||
configured Weather API endpoint and assembles the bundle consumed by forecast
|
configured Weather API endpoint and assembles the bundle consumed by forecast
|
||||||
derivation and briefing builders.
|
derivation and briefing builders. Briefing builders expose normalized current
|
||||||
|
conditions as prompt context when `/conditions/current` is available.
|
||||||
|
|
||||||
## Inputs And Outputs
|
## Inputs And Outputs
|
||||||
|
|
||||||
|
|||||||
@@ -206,6 +206,10 @@ func TestGenerateReportWritesReportAndPreflight(t *testing.T) {
|
|||||||
if location == nil || location.ID != "home" || location.Name != "Brentwood" || location.Region != "St. Louis Metro" || location.Timezone != "America/Chicago" {
|
if location == nil || location.ID != "home" || location.Name != "Brentwood" || location.Region != "St. Louis Metro" || location.Timezone != "America/Chicago" {
|
||||||
t.Fatalf("data package location = %#v, want configured prompt location", location)
|
t.Fatalf("data package location = %#v, want configured prompt location", location)
|
||||||
}
|
}
|
||||||
|
current := savedDataPackage.Briefing.CurrentConditions
|
||||||
|
if current == nil || current.ConditionText != "Clear" || current.TemperatureF == nil || *current.TemperatureF != 75 {
|
||||||
|
t.Fatalf("data package current conditions = %#v, want current conditions", current)
|
||||||
|
}
|
||||||
if !strings.Contains(string(data), "Short-term AFD narrative for generated report.") || !strings.Contains(string(data), "Long-term AFD narrative for generated report.") {
|
if !strings.Contains(string(data), "Short-term AFD narrative for generated report.") || !strings.Contains(string(data), "Long-term AFD narrative for generated report.") {
|
||||||
t.Fatalf("data package missing AFD short/long-term discussion:\n%s", string(data))
|
t.Fatalf("data package missing AFD short/long-term discussion:\n%s", string(data))
|
||||||
}
|
}
|
||||||
@@ -887,7 +891,7 @@ func dailyBundleServer(t *testing.T) *httptest.Server {
|
|||||||
case "/observations":
|
case "/observations":
|
||||||
_, _ = w.Write([]byte(`{"data":{"timestamp":"2026-05-29T14:00:00Z","conditionCode":3}}`))
|
_, _ = w.Write([]byte(`{"data":{"timestamp":"2026-05-29T14:00:00Z","conditionCode":3}}`))
|
||||||
case "/conditions/current":
|
case "/conditions/current":
|
||||||
_, _ = w.Write([]byte(`{"data":{"conditionText":"Clear"}}`))
|
_, _ = w.Write([]byte(`{"data":{"conditionText":"Clear","temperatureF":75,"relativeHumidityPercent":56,"windSpeedMph":8}}`))
|
||||||
case "/forecast/hourly":
|
case "/forecast/hourly":
|
||||||
_, _ = w.Write([]byte(`{"data":{"locationId":"test-grid","locationName":"Testville","issuedAt":"2026-05-29T10:30:00-05:00","product":"hourly","periods":[{"startTime":"2026-05-29T06:00:00-05:00","endTime":"2026-05-29T07:00:00-05:00","textDescription":"Showers and thunderstorms","temperatureF":66,"probabilityOfPrecipitationPercent":80,"windGustMph":32},{"startTime":"2026-05-30T06:00:00-05:00","endTime":"2026-05-30T07:00:00-05:00","textDescription":"Showers and thunderstorms","temperatureF":66,"probabilityOfPrecipitationPercent":80,"windGustMph":32}]}}`))
|
_, _ = w.Write([]byte(`{"data":{"locationId":"test-grid","locationName":"Testville","issuedAt":"2026-05-29T10:30:00-05:00","product":"hourly","periods":[{"startTime":"2026-05-29T06:00:00-05:00","endTime":"2026-05-29T07:00:00-05:00","textDescription":"Showers and thunderstorms","temperatureF":66,"probabilityOfPrecipitationPercent":80,"windGustMph":32},{"startTime":"2026-05-30T06:00:00-05:00","endTime":"2026-05-30T07:00:00-05:00","textDescription":"Showers and thunderstorms","temperatureF":66,"probabilityOfPrecipitationPercent":80,"windGustMph":32}]}}`))
|
||||||
case "/forecast/narrative":
|
case "/forecast/narrative":
|
||||||
|
|||||||
@@ -68,9 +68,8 @@ func BuildDaily(ctx BuildContext, summary *forecast.DailySummary) (Package, erro
|
|||||||
if summary == nil {
|
if summary == nil {
|
||||||
return Package{}, fmt.Errorf("daily forecast summary is required")
|
return Package{}, fmt.Errorf("daily forecast summary is required")
|
||||||
}
|
}
|
||||||
pkg := Package{
|
pkg := buildPackage(ctx)
|
||||||
Metadata: BuildMetadata(ctx),
|
pkg.Daily = &Daily{
|
||||||
Daily: &Daily{
|
|
||||||
BottomLine: buildBottomLine(summary),
|
BottomLine: buildBottomLine(summary),
|
||||||
Dayparts: summary.Dayparts,
|
Dayparts: summary.Dayparts,
|
||||||
RelevantAlerts: summary.AlertOverlaps,
|
RelevantAlerts: summary.AlertOverlaps,
|
||||||
@@ -79,7 +78,6 @@ func BuildDaily(ctx BuildContext, summary *forecast.DailySummary) (Package, erro
|
|||||||
Discussion: buildDiscussion(summary.Discussion),
|
Discussion: buildDiscussion(summary.Discussion),
|
||||||
WeatherStory: buildWeatherStory(ctx.Bundle),
|
WeatherStory: buildWeatherStory(ctx.Bundle),
|
||||||
ForecastSummaryDate: summary.Date,
|
ForecastSummaryDate: summary.Date,
|
||||||
},
|
|
||||||
}
|
}
|
||||||
setRelevantAlertCount(&pkg.Metadata, len(summary.AlertOverlaps))
|
setRelevantAlertCount(&pkg.Metadata, len(summary.AlertOverlaps))
|
||||||
if ctx.Resolved.Definition.ID == report.DailyTomorrow {
|
if ctx.Resolved.Definition.ID == report.DailyTomorrow {
|
||||||
|
|||||||
@@ -15,6 +15,19 @@ import (
|
|||||||
|
|
||||||
func TestDailyBriefingFromRepresentativeFixture(t *testing.T) {
|
func TestDailyBriefingFromRepresentativeFixture(t *testing.T) {
|
||||||
bundle := loadBundleFixture(t)
|
bundle := loadBundleFixture(t)
|
||||||
|
currentIsDay := true
|
||||||
|
currentTemp := 75.9
|
||||||
|
currentFeelsLike := 76.1
|
||||||
|
currentHumidity := 56.0
|
||||||
|
currentWind := 10.7
|
||||||
|
bundle.Current = &forecast.Current{
|
||||||
|
ConditionText: "Partly cloudy",
|
||||||
|
IsDay: ¤tIsDay,
|
||||||
|
TemperatureF: ¤tTemp,
|
||||||
|
ApparentTemperatureF: ¤tFeelsLike,
|
||||||
|
RelativeHumidityPercent: ¤tHumidity,
|
||||||
|
WindSpeedMph: ¤tWind,
|
||||||
|
}
|
||||||
bundle.Sources[0].DataSHA256 = "abc123"
|
bundle.Sources[0].DataSHA256 = "abc123"
|
||||||
bundle.Warnings = []forecast.SourceWarning{{Source: "daily", Code: "missing_source", Severity: "warning"}}
|
bundle.Warnings = []forecast.SourceWarning{{Source: "daily", Code: "missing_source", Severity: "warning"}}
|
||||||
location := mustLocation(t)
|
location := mustLocation(t)
|
||||||
@@ -55,6 +68,9 @@ func TestDailyBriefingFromRepresentativeFixture(t *testing.T) {
|
|||||||
if pkg.Metadata.Location == nil || pkg.Metadata.Location.ID != "home" || pkg.Metadata.Location.Name != "Brentwood" || pkg.Metadata.Location.Region != "St. Louis Metro" || pkg.Metadata.Location.Timezone != "America/Chicago" {
|
if pkg.Metadata.Location == nil || pkg.Metadata.Location.ID != "home" || pkg.Metadata.Location.Name != "Brentwood" || pkg.Metadata.Location.Region != "St. Louis Metro" || pkg.Metadata.Location.Timezone != "America/Chicago" {
|
||||||
t.Fatalf("metadata location = %#v, want configured prompt location", pkg.Metadata.Location)
|
t.Fatalf("metadata location = %#v, want configured prompt location", pkg.Metadata.Location)
|
||||||
}
|
}
|
||||||
|
if pkg.CurrentConditions == nil || pkg.CurrentConditions.ConditionText != "Partly cloudy" || pkg.CurrentConditions.TemperatureF == nil || *pkg.CurrentConditions.TemperatureF != currentTemp || pkg.CurrentConditions.RelativeHumidityPercent == nil || *pkg.CurrentConditions.RelativeHumidityPercent != currentHumidity {
|
||||||
|
t.Fatalf("CurrentConditions = %#v, want current conditions from bundle", pkg.CurrentConditions)
|
||||||
|
}
|
||||||
if len(pkg.Metadata.Sources) != 1 || pkg.Metadata.Sources[0].DataSHA256 != "abc123" {
|
if len(pkg.Metadata.Sources) != 1 || pkg.Metadata.Sources[0].DataSHA256 != "abc123" {
|
||||||
t.Fatalf("Sources = %#v, want source hash", pkg.Metadata.Sources)
|
t.Fatalf("Sources = %#v, want source hash", pkg.Metadata.Sources)
|
||||||
}
|
}
|
||||||
@@ -104,7 +120,9 @@ func TestDailyBriefingQuietWeather(t *testing.T) {
|
|||||||
Sources: []forecast.Source{
|
Sources: []forecast.Source{
|
||||||
{Name: "hourly", FetchedAt: time.Now()},
|
{Name: "hourly", FetchedAt: time.Now()},
|
||||||
{Name: "alerts", Endpoint: "/alerts/active", 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"}},
|
||||||
}
|
}
|
||||||
summary, err := forecast.BuildDailySummary(bundle, resolved.ValidPeriod.Start, location, defaultDayparts())
|
summary, err := forecast.BuildDailySummary(bundle, resolved.ValidPeriod.Start, location, defaultDayparts())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -117,6 +135,12 @@ func TestDailyBriefingQuietWeather(t *testing.T) {
|
|||||||
if pkg.Daily.BottomLine.Summary != "Conditions: Clear." {
|
if pkg.Daily.BottomLine.Summary != "Conditions: Clear." {
|
||||||
t.Fatalf("BottomLine summary = %q, want clear conditions", pkg.Daily.BottomLine.Summary)
|
t.Fatalf("BottomLine summary = %q, want clear conditions", pkg.Daily.BottomLine.Summary)
|
||||||
}
|
}
|
||||||
|
if pkg.CurrentConditions != nil {
|
||||||
|
t.Fatalf("CurrentConditions = %#v, want nil when current conditions are missing", pkg.CurrentConditions)
|
||||||
|
}
|
||||||
|
if len(pkg.Metadata.SourceWarnings) != 1 || pkg.Metadata.SourceWarnings[0].Source != "current" {
|
||||||
|
t.Fatalf("SourceWarnings = %#v, want current missing-source warning", pkg.Metadata.SourceWarnings)
|
||||||
|
}
|
||||||
if len(pkg.Daily.RelevantAlerts) != 0 {
|
if len(pkg.Daily.RelevantAlerts) != 0 {
|
||||||
t.Fatalf("RelevantAlerts length = %d, want 0", len(pkg.Daily.RelevantAlerts))
|
t.Fatalf("RelevantAlerts length = %d, want 0", len(pkg.Daily.RelevantAlerts))
|
||||||
}
|
}
|
||||||
@@ -126,12 +150,12 @@ func TestDailyBriefingQuietWeather(t *testing.T) {
|
|||||||
if !pkg.Metadata.Alerts.Checked || pkg.Metadata.Alerts.ActiveCount != 0 || pkg.Metadata.Alerts.RelevantCount != 0 || pkg.Metadata.Alerts.Missing {
|
if !pkg.Metadata.Alerts.Checked || pkg.Metadata.Alerts.ActiveCount != 0 || pkg.Metadata.Alerts.RelevantCount != 0 || pkg.Metadata.Alerts.Missing {
|
||||||
t.Fatalf("Metadata.Alerts = %#v, want checked no-active-alerts status", pkg.Metadata.Alerts)
|
t.Fatalf("Metadata.Alerts = %#v, want checked no-active-alerts status", pkg.Metadata.Alerts)
|
||||||
}
|
}
|
||||||
data, err := json.Marshal(pkg.Metadata)
|
data, err := json.Marshal(pkg.Metadata.Alerts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("marshal metadata: %v", err)
|
t.Fatalf("marshal alert metadata: %v", err)
|
||||||
}
|
}
|
||||||
if strings.Contains(string(data), `"missing"`) {
|
if strings.Contains(string(data), `"missing"`) {
|
||||||
t.Fatalf("metadata includes missing for checked empty alerts:\n%s", string(data))
|
t.Fatalf("alert metadata includes missing for checked empty alerts:\n%s", string(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const SchemaVersion = "weatherreporter.briefing.v1"
|
|||||||
|
|
||||||
type Package struct {
|
type Package struct {
|
||||||
Metadata Metadata `json:"metadata"`
|
Metadata Metadata `json:"metadata"`
|
||||||
|
CurrentConditions *CurrentConditionsContext `json:"currentConditions,omitempty"`
|
||||||
Daily *Daily `json:"daily,omitempty"`
|
Daily *Daily `json:"daily,omitempty"`
|
||||||
ThreeDay *ThreeDay `json:"threeDay,omitempty"`
|
ThreeDay *ThreeDay `json:"threeDay,omitempty"`
|
||||||
Weekend *Weekend `json:"weekend,omitempty"`
|
Weekend *Weekend `json:"weekend,omitempty"`
|
||||||
@@ -46,6 +47,21 @@ type LocationContext struct {
|
|||||||
Timezone string `json:"timezone,omitempty"`
|
Timezone string `json:"timezone,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CurrentConditionsContext struct {
|
||||||
|
ConditionText string `json:"conditionText,omitempty"`
|
||||||
|
IsDay *bool `json:"isDay,omitempty"`
|
||||||
|
TemperatureC *float64 `json:"temperatureC,omitempty"`
|
||||||
|
TemperatureF *float64 `json:"temperatureF,omitempty"`
|
||||||
|
ApparentTemperatureC *float64 `json:"apparentTemperatureC,omitempty"`
|
||||||
|
ApparentTemperatureF *float64 `json:"apparentTemperatureF,omitempty"`
|
||||||
|
DewpointC *float64 `json:"dewpointC,omitempty"`
|
||||||
|
DewpointF *float64 `json:"dewpointF,omitempty"`
|
||||||
|
RelativeHumidityPercent *float64 `json:"relativeHumidityPercent,omitempty"`
|
||||||
|
WindSpeedKmh *float64 `json:"windSpeedKmh,omitempty"`
|
||||||
|
WindSpeedMph *float64 `json:"windSpeedMph,omitempty"`
|
||||||
|
WindDirectionDegrees *float64 `json:"windDirectionDegrees,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type SourceMetadata struct {
|
type SourceMetadata struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Endpoint string `json:"endpoint,omitempty"`
|
Endpoint string `json:"endpoint,omitempty"`
|
||||||
@@ -94,6 +110,13 @@ func BuildMetadata(ctx BuildContext) Metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildPackage(ctx BuildContext) Package {
|
||||||
|
return Package{
|
||||||
|
Metadata: BuildMetadata(ctx),
|
||||||
|
CurrentConditions: currentConditions(ctx.Bundle),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func copyLocation(location *LocationContext) *LocationContext {
|
func copyLocation(location *LocationContext) *LocationContext {
|
||||||
if location == nil {
|
if location == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -102,6 +125,58 @@ func copyLocation(location *LocationContext) *LocationContext {
|
|||||||
return &copied
|
return &copied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func currentConditions(bundle *forecast.Bundle) *CurrentConditionsContext {
|
||||||
|
if bundle == nil || bundle.Current == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
current := bundle.Current
|
||||||
|
context := CurrentConditionsContext{
|
||||||
|
ConditionText: current.ConditionText,
|
||||||
|
IsDay: copyBool(current.IsDay),
|
||||||
|
TemperatureC: copyFloat(current.TemperatureC),
|
||||||
|
TemperatureF: copyFloat(current.TemperatureF),
|
||||||
|
ApparentTemperatureC: copyFloat(current.ApparentTemperatureC),
|
||||||
|
ApparentTemperatureF: copyFloat(current.ApparentTemperatureF),
|
||||||
|
DewpointC: copyFloat(current.DewpointC),
|
||||||
|
DewpointF: copyFloat(current.DewpointF),
|
||||||
|
RelativeHumidityPercent: copyFloat(current.RelativeHumidityPercent),
|
||||||
|
WindSpeedKmh: copyFloat(current.WindSpeedKmh),
|
||||||
|
WindSpeedMph: copyFloat(current.WindSpeedMph),
|
||||||
|
WindDirectionDegrees: copyFloat(current.WindDirectionDegrees),
|
||||||
|
}
|
||||||
|
if context.ConditionText == "" &&
|
||||||
|
context.IsDay == nil &&
|
||||||
|
context.TemperatureC == nil &&
|
||||||
|
context.TemperatureF == nil &&
|
||||||
|
context.ApparentTemperatureC == nil &&
|
||||||
|
context.ApparentTemperatureF == nil &&
|
||||||
|
context.DewpointC == nil &&
|
||||||
|
context.DewpointF == nil &&
|
||||||
|
context.RelativeHumidityPercent == nil &&
|
||||||
|
context.WindSpeedKmh == nil &&
|
||||||
|
context.WindSpeedMph == nil &&
|
||||||
|
context.WindDirectionDegrees == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &context
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyBool(value *bool) *bool {
|
||||||
|
if value == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
copied := *value
|
||||||
|
return &copied
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFloat(value *float64) *float64 {
|
||||||
|
if value == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
copied := *value
|
||||||
|
return &copied
|
||||||
|
}
|
||||||
|
|
||||||
func Save(path string, pkg Package) error {
|
func Save(path string, pkg Package) error {
|
||||||
if err := fileutil.WriteJSONAtomic(path, pkg); err != nil {
|
if err := fileutil.WriteJSONAtomic(path, pkg); err != nil {
|
||||||
return fmt.Errorf("save briefing package: %w", err)
|
return fmt.Errorf("save briefing package: %w", err)
|
||||||
|
|||||||
@@ -56,10 +56,8 @@ func BuildStorm(ctx BuildContext) (Package, error) {
|
|||||||
Discussion: buildDiscussion(ctx.Bundle.Discussion),
|
Discussion: buildDiscussion(ctx.Bundle.Discussion),
|
||||||
WeatherStory: buildWeatherStory(ctx.Bundle),
|
WeatherStory: buildWeatherStory(ctx.Bundle),
|
||||||
}
|
}
|
||||||
pkg := Package{
|
pkg := buildPackage(ctx)
|
||||||
Metadata: BuildMetadata(ctx),
|
pkg.Storm = storm
|
||||||
Storm: storm,
|
|
||||||
}
|
|
||||||
setRelevantAlertCount(&pkg.Metadata, len(alerts))
|
setRelevantAlertCount(&pkg.Metadata, len(alerts))
|
||||||
return pkg, nil
|
return pkg, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,12 +37,10 @@ func BuildThreeDay(ctx BuildContext, summaries []forecast.DailySummary) (Package
|
|||||||
if len(summaries) == 0 {
|
if len(summaries) == 0 {
|
||||||
return Package{}, fmt.Errorf("3-day forecast summaries are required")
|
return Package{}, fmt.Errorf("3-day forecast summaries are required")
|
||||||
}
|
}
|
||||||
pkg := Package{
|
pkg := buildPackage(ctx)
|
||||||
Metadata: BuildMetadata(ctx),
|
pkg.ThreeDay = &ThreeDay{
|
||||||
ThreeDay: &ThreeDay{
|
|
||||||
Discussion: buildDiscussion(summaries[0].Discussion),
|
Discussion: buildDiscussion(summaries[0].Discussion),
|
||||||
WeatherStory: buildWeatherStory(ctx.Bundle),
|
WeatherStory: buildWeatherStory(ctx.Bundle),
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, summary := range summaries {
|
for _, summary := range summaries {
|
||||||
day := buildOutlookDay(summary)
|
day := buildOutlookDay(summary)
|
||||||
|
|||||||
@@ -31,12 +31,10 @@ func BuildWeekend(ctx BuildContext, summaries []forecast.DailySummary) (Package,
|
|||||||
if len(summaries) == 0 {
|
if len(summaries) == 0 {
|
||||||
return Package{}, fmt.Errorf("weekend forecast summaries are required")
|
return Package{}, fmt.Errorf("weekend forecast summaries are required")
|
||||||
}
|
}
|
||||||
pkg := Package{
|
pkg := buildPackage(ctx)
|
||||||
Metadata: BuildMetadata(ctx),
|
pkg.Weekend = &Weekend{
|
||||||
Weekend: &Weekend{
|
|
||||||
Discussion: buildDiscussion(summaries[0].Discussion),
|
Discussion: buildDiscussion(summaries[0].Discussion),
|
||||||
WeatherStory: buildWeatherStory(ctx.Bundle),
|
WeatherStory: buildWeatherStory(ctx.Bundle),
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, summary := range summaries {
|
for _, summary := range summaries {
|
||||||
pkg.Weekend.Days = append(pkg.Weekend.Days, buildOutlookDay(summary))
|
pkg.Weekend.Days = append(pkg.Weekend.Days, buildOutlookDay(summary))
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ func TestBuildDailyDataPackage(t *testing.T) {
|
|||||||
if pkg.Briefing.Metadata.Location == nil || pkg.Briefing.Metadata.Location.Name != "Brentwood" {
|
if pkg.Briefing.Metadata.Location == nil || pkg.Briefing.Metadata.Location.Name != "Brentwood" {
|
||||||
t.Fatalf("Briefing.Metadata.Location = %#v, want configured location", pkg.Briefing.Metadata.Location)
|
t.Fatalf("Briefing.Metadata.Location = %#v, want configured location", pkg.Briefing.Metadata.Location)
|
||||||
}
|
}
|
||||||
|
if pkg.Briefing.CurrentConditions == nil || pkg.Briefing.CurrentConditions.ConditionText != "Partly cloudy" {
|
||||||
|
t.Fatalf("Briefing.CurrentConditions = %#v, want current conditions", pkg.Briefing.CurrentConditions)
|
||||||
|
}
|
||||||
if pkg.RecentChanges.Items == nil || len(pkg.RecentChanges.Items) != 0 {
|
if pkg.RecentChanges.Items == nil || len(pkg.RecentChanges.Items) != 0 {
|
||||||
t.Fatalf("RecentChanges.Items = %#v, want empty slice", pkg.RecentChanges.Items)
|
t.Fatalf("RecentChanges.Items = %#v, want empty slice", pkg.RecentChanges.Items)
|
||||||
}
|
}
|
||||||
@@ -169,6 +172,9 @@ func validBriefingPackage() briefing.Package {
|
|||||||
End: time.Date(2026, 5, 30, 5, 0, 0, 0, time.UTC),
|
End: time.Date(2026, 5, 30, 5, 0, 0, 0, time.UTC),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
CurrentConditions: &briefing.CurrentConditionsContext{
|
||||||
|
ConditionText: "Partly cloudy",
|
||||||
|
},
|
||||||
Daily: &briefing.Daily{
|
Daily: &briefing.Daily{
|
||||||
ForecastSummaryDate: "2026-05-29",
|
ForecastSummaryDate: "2026-05-29",
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user