diff --git a/internal/adapters/inbound/httpapi/presenter/outlook.go b/internal/adapters/inbound/httpapi/presenter/outlook.go index a145b89..bab5520 100644 --- a/internal/adapters/inbound/httpapi/presenter/outlook.go +++ b/internal/adapters/inbound/httpapi/presenter/outlook.go @@ -22,10 +22,14 @@ func OutlookRunPayload(run *model.WeatherOutlookRun, _ Units, tz *time.Location) AsOf: inLocationTime(run.AsOf, tz), IssuedAt: inLocationTimePtr(run.IssuedAt, tz), Outlooks: make([]model.WeatherOutlook, 0, len(run.Outlooks)), + Discussions: make([]model.WeatherOutlookDiscussion, 0, len(run.Discussions)), } for _, outlook := range run.Outlooks { out.Outlooks = append(out.Outlooks, copyOutlook(outlook, tz)) } + for _, discussion := range run.Discussions { + out.Discussions = append(out.Discussions, copyOutlookDiscussion(discussion, tz)) + } return &out } @@ -54,6 +58,16 @@ func copyOutlook(outlook model.WeatherOutlook, tz *time.Location) model.WeatherO return out } +func copyOutlookDiscussion(discussion model.WeatherOutlookDiscussion, tz *time.Location) model.WeatherOutlookDiscussion { + return model.WeatherOutlookDiscussion{ + Day: discussion.Day, + Headline: discussion.Headline, + Summary: discussion.Summary, + Discussion: discussion.Discussion, + UpdatedAt: inLocationTimePtr(discussion.UpdatedAt, tz), + } +} + func copyIntPtr(v *int) *int { if v == nil { return nil diff --git a/internal/adapters/inbound/httpapi/presenter/payload_test.go b/internal/adapters/inbound/httpapi/presenter/payload_test.go index 7984e3e..d4e875b 100644 --- a/internal/adapters/inbound/httpapi/presenter/payload_test.go +++ b/internal/adapters/inbound/httpapi/presenter/payload_test.go @@ -440,6 +440,7 @@ func TestOutlookRunPayloadTimezoneConversionAndCopySemantics(t *testing.T) { loc := time.FixedZone("UTC-05:00", -5*60*60) asOf := time.Date(2026, 6, 11, 18, 0, 0, 0, time.UTC) issuedAt := asOf.Add(-1 * time.Hour) + discussionUpdatedAt := asOf.Add(-30 * time.Minute) severityRank := 5 latitude := 38.627123 longitude := -90.199456 @@ -470,6 +471,13 @@ func TestOutlookRunPayloadTimezoneConversionAndCopySemantics(t *testing.T) { ContainsLocation: true, Geometry: geometry, }}, + Discussions: []model.WeatherOutlookDiscussion{{ + Day: 1, + Headline: "Severe storms possible", + Summary: "Scattered severe storms are possible.", + Discussion: "Discussion text.", + UpdatedAt: &discussionUpdatedAt, + }}, } payload := OutlookRunPayload(run, UnitsUS, loc) @@ -486,9 +494,15 @@ func TestOutlookRunPayloadTimezoneConversionAndCopySemantics(t *testing.T) { if len(out.Outlooks) != 1 { t.Fatalf("expected one outlook, got %d", len(out.Outlooks)) } + if len(out.Discussions) != 1 { + t.Fatalf("expected one discussion, got %d", len(out.Discussions)) + } if out.Outlooks[0].SeverityRank == run.Outlooks[0].SeverityRank { t.Fatalf("expected severity rank pointer to be copied") } + if out.Discussions[0].UpdatedAt == run.Discussions[0].UpdatedAt { + t.Fatalf("expected discussion updatedAt pointer to be copied") + } if &out.Outlooks[0].Geometry[0] == &run.Outlooks[0].Geometry[0] { t.Fatalf("expected geometry bytes to be copied") } @@ -499,6 +513,7 @@ func TestOutlookRunPayloadTimezoneConversionAndCopySemantics(t *testing.T) { assertOffsetSeconds(t, out.Outlooks[0].ValidTo, -5*60*60) assertOffsetSeconds(t, out.Outlooks[0].IssuedAt, -5*60*60) assertOffsetSeconds(t, out.Outlooks[0].ExpiresAt, -5*60*60) + assertOffsetSeconds(t, *out.Discussions[0].UpdatedAt, -5*60*60) if !out.AsOf.UTC().Equal(asOf) || !out.Outlooks[0].ValidFrom.UTC().Equal(asOf) { t.Fatalf("expected timezone conversion to preserve instants") } @@ -512,12 +527,19 @@ func TestOutlookRunPayloadTimezoneConversionAndCopySemantics(t *testing.T) { if string(out.Outlooks[0].Geometry) != string(geometry) { t.Fatalf("expected geometry bytes preserved, got %s", out.Outlooks[0].Geometry) } + if out.Discussions[0].Day != 1 || out.Discussions[0].Headline != "Severe storms possible" || + out.Discussions[0].Summary != "Scattered severe storms are possible." || + out.Discussions[0].Discussion != "Discussion text." { + t.Fatalf("expected discussion fields preserved, got %+v", out.Discussions[0]) + } *out.Latitude = 99 *out.Longitude = -99 *out.IssuedAt = time.Date(2030, 1, 1, 0, 0, 0, 0, time.UTC) *out.Outlooks[0].SeverityRank = 99 + *out.Discussions[0].UpdatedAt = time.Date(2031, 1, 1, 0, 0, 0, 0, time.UTC) out.Outlooks[0].Geometry[0] = '[' + out.Discussions[0].Headline = "changed" if *run.Latitude != latitude || *run.Longitude != longitude || !run.IssuedAt.Equal(issuedAt) { t.Fatalf("expected source run pointers not to mutate") } @@ -527,8 +549,15 @@ func TestOutlookRunPayloadTimezoneConversionAndCopySemantics(t *testing.T) { if string(run.Outlooks[0].Geometry) != string(geometry) { t.Fatalf("expected source geometry not to mutate, got %s", run.Outlooks[0].Geometry) } + if !run.Discussions[0].UpdatedAt.Equal(discussionUpdatedAt) { + t.Fatalf("expected source discussion updatedAt not to mutate, got %v", run.Discussions[0].UpdatedAt) + } + if run.Discussions[0].Headline != "Severe storms possible" { + t.Fatalf("expected source discussion headline not to mutate, got %q", run.Discussions[0].Headline) + } assertOffsetSeconds(t, run.AsOf, 0) assertOffsetSeconds(t, run.Outlooks[0].ValidFrom, 0) + assertOffsetSeconds(t, *run.Discussions[0].UpdatedAt, 0) } func float64Ptr(v float64) *float64 { diff --git a/templates/outlooks_convective.txt.tmpl b/templates/outlooks_convective.txt.tmpl index 6512d80..2de2d5f 100644 --- a/templates/outlooks_convective.txt.tmpl +++ b/templates/outlooks_convective.txt.tmpl @@ -19,6 +19,24 @@ Label Text: {{$outlook.LabelText}} Source URL: {{$outlook.SourceURL}} {{- end}} {{- end}} + +Discussions: {{len .Data.Discussions}} +{{- range $i, $discussion := .Data.Discussions}} + +[{{$i}}] Day {{$discussion.Day}} Discussion +{{- if $discussion.UpdatedAt}} +Updated At: {{$discussion.UpdatedAt}} +{{- end}} +{{- if $discussion.Headline}} +Headline: {{$discussion.Headline}} +{{- end}} +{{- if $discussion.Summary}} +Summary: {{$discussion.Summary}} +{{- end}} +{{- if $discussion.Discussion}} +Discussion: {{$discussion.Discussion}} +{{- end}} +{{- end}} {{- else -}} No convective outlook data available. {{- end}}