Add Today generated text assets

This commit is contained in:
2026-06-15 14:45:49 +00:00
parent 4e704e4f51
commit 8ff5c44324
14 changed files with 1023 additions and 22 deletions

View File

@@ -52,6 +52,46 @@ type TomorrowRenderContext struct {
Derived facts.DerivedFacts
}
type TodayRenderContext struct {
Report TodayReportContext
GeneratedText Today
Modules TodayTemplateModules
Collected facts.CollectedFacts
Derived facts.DerivedFacts
}
type TodayReportContext struct {
Title string
ForecastDate time.Time
ForecastDateLabel string
ForecastDayName string
GeneratedAt time.Time
GeneratedAtLabel string
ValidPeriod timeutil.Period
Timezone string
}
type TodayTemplateModules struct {
Metadata *briefing.MetadataModule
CurrentConditions *briefing.CurrentConditionsModule
HourlyForecast *briefing.HourlyForecastModule
DerivedDailySummary *briefing.DerivedDailySummaryModule
DerivedDaypartSummaries *map[string]briefing.DerivedDaypartSummaryModule
Dayparts []TodayDaypartContext
PrecipTiming *briefing.PrecipTimingModule
AlertDigest *briefing.AlertDigestModule
SPCConvectiveOutlooks *briefing.SPCConvectiveOutlooksModule
AreaForecastDiscussion *briefing.AreaForecastDiscussionModule
SPCConvectiveDiscussion *briefing.SPCConvectiveDiscussionModule
WeatherStory *briefing.WeatherStoryModule
TodayPlanning *briefing.TodayPlanningModule
}
type TodayDaypartContext struct {
Key string
Summary briefing.DerivedDaypartSummaryModule
}
type TomorrowReportContext struct {
Title string
ForecastDate time.Time
@@ -116,6 +156,40 @@ func BuildHourlyRenderContext(metadata briefing.Metadata, snapshot module.Snapsh
}, nil
}
func BuildTodayRenderContext(metadata briefing.Metadata, snapshot module.Snapshot, generated Today, collected facts.CollectedFacts, derived facts.DerivedFacts) (TodayRenderContext, error) {
location, err := timeutil.LoadLocation(metadata.Timezone)
if err != nil {
return TodayRenderContext{}, fmt.Errorf("build today render context: %w", err)
}
if metadata.GeneratedAt.IsZero() {
return TodayRenderContext{}, fmt.Errorf("build today render context: generatedAt is required")
}
if !metadata.ValidPeriod.IsValid() {
return TodayRenderContext{}, fmt.Errorf("build today render context: valid period is required")
}
modules, err := todayTemplateModules(snapshot, derived)
if err != nil {
return TodayRenderContext{}, err
}
forecastDate := metadata.ValidPeriod.Start.In(location)
return TodayRenderContext{
Report: TodayReportContext{
Title: "Today's Weather",
ForecastDate: forecastDate,
ForecastDateLabel: forecastDate.Format("Monday, January 2, 2006"),
ForecastDayName: forecastDate.Format("Monday"),
GeneratedAt: metadata.GeneratedAt,
GeneratedAtLabel: generatedAtLabel(metadata.GeneratedAt, location),
ValidPeriod: metadata.ValidPeriod,
Timezone: metadata.Timezone,
},
GeneratedText: generated,
Modules: modules,
Collected: collected,
Derived: derived,
}, nil
}
func BuildTomorrowRenderContext(metadata briefing.Metadata, snapshot module.Snapshot, generated Tomorrow, collected facts.CollectedFacts, derived facts.DerivedFacts) (TomorrowRenderContext, error) {
location, err := timeutil.LoadLocation(metadata.Timezone)
if err != nil {
@@ -151,6 +225,73 @@ func BuildTomorrowRenderContext(metadata briefing.Metadata, snapshot module.Snap
}, nil
}
func todayTemplateModules(snapshot module.Snapshot, derived facts.DerivedFacts) (TodayTemplateModules, error) {
lookup := newModuleSnapshotLookup(snapshot)
metadata, err := lookup.metadata()
if err != nil {
return TodayTemplateModules{}, err
}
current, err := lookup.currentConditions()
if err != nil {
return TodayTemplateModules{}, err
}
hourly, err := lookup.hourlyForecast()
if err != nil {
return TodayTemplateModules{}, err
}
daily, err := lookup.derivedDailySummary()
if err != nil {
return TodayTemplateModules{}, err
}
dayparts, err := lookup.derivedDaypartSummaries()
if err != nil {
return TodayTemplateModules{}, err
}
precip, err := lookup.precipTiming()
if err != nil {
return TodayTemplateModules{}, err
}
alerts, err := lookup.alertDigest()
if err != nil {
return TodayTemplateModules{}, err
}
outlooks, err := lookup.spcConvectiveOutlooks()
if err != nil {
return TodayTemplateModules{}, err
}
discussion, err := lookup.areaForecastDiscussion()
if err != nil {
return TodayTemplateModules{}, err
}
spcDiscussion, err := lookup.spcConvectiveDiscussion()
if err != nil {
return TodayTemplateModules{}, err
}
story, err := lookup.weatherStory()
if err != nil {
return TodayTemplateModules{}, err
}
planning, err := lookup.todayPlanning()
if err != nil {
return TodayTemplateModules{}, err
}
return TodayTemplateModules{
Metadata: metadata,
CurrentConditions: current,
HourlyForecast: hourly,
DerivedDailySummary: daily,
DerivedDaypartSummaries: dayparts,
Dayparts: orderedTodayDayparts(dayparts, derived.DaypartSummaries),
PrecipTiming: precip,
AlertDigest: alerts,
SPCConvectiveOutlooks: outlooks,
AreaForecastDiscussion: discussion,
SPCConvectiveDiscussion: spcDiscussion,
WeatherStory: story,
TodayPlanning: planning,
}, nil
}
func hourlyTemplateModules(snapshot module.Snapshot) (HourlyTemplateModules, error) {
lookup := newModuleSnapshotLookup(snapshot)
metadata, err := lookup.metadata()
@@ -329,6 +470,10 @@ func (lookup moduleSnapshotLookup) weatherStory() (*briefing.WeatherStoryModule,
return optionalStanza[briefing.WeatherStoryModule](lookup, module.WeatherStory)
}
func (lookup moduleSnapshotLookup) todayPlanning() (*briefing.TodayPlanningModule, error) {
return optionalStanza[briefing.TodayPlanningModule](lookup, module.TodayPlanning)
}
func (lookup moduleSnapshotLookup) tomorrowPlanning() (*briefing.TomorrowPlanningModule, error) {
return optionalStanza[briefing.TomorrowPlanningModule](lookup, module.TomorrowPlanning)
}
@@ -346,11 +491,34 @@ func optionalStanza[T any](lookup moduleSnapshotLookup, id module.ID) (*T, error
return &value, nil
}
func orderedTodayDayparts(dayparts *map[string]briefing.DerivedDaypartSummaryModule, ordered []forecast.DaypartSummary) []TodayDaypartContext {
orderedRows := orderedDaypartRows(dayparts, ordered)
out := make([]TodayDaypartContext, 0, len(orderedRows))
for _, row := range orderedRows {
out = append(out, TodayDaypartContext{Key: row.Key, Summary: row.Summary})
}
return out
}
func orderedTomorrowDayparts(dayparts *map[string]briefing.DerivedDaypartSummaryModule, ordered []forecast.DaypartSummary) []TomorrowDaypartContext {
orderedRows := orderedDaypartRows(dayparts, ordered)
out := make([]TomorrowDaypartContext, 0, len(orderedRows))
for _, row := range orderedRows {
out = append(out, TomorrowDaypartContext{Key: row.Key, Summary: row.Summary})
}
return out
}
type orderedDaypartRow struct {
Key string
Summary briefing.DerivedDaypartSummaryModule
}
func orderedDaypartRows(dayparts *map[string]briefing.DerivedDaypartSummaryModule, ordered []forecast.DaypartSummary) []orderedDaypartRow {
if dayparts == nil || len(*dayparts) == 0 {
return nil
}
out := make([]TomorrowDaypartContext, 0, len(*dayparts))
out := make([]orderedDaypartRow, 0, len(*dayparts))
seen := map[string]struct{}{}
for _, daypart := range ordered {
for _, key := range daypartModuleKeyCandidates(daypart) {
@@ -362,7 +530,7 @@ func orderedTomorrowDayparts(dayparts *map[string]briefing.DerivedDaypartSummary
continue
}
seen[key] = struct{}{}
out = append(out, TomorrowDaypartContext{Key: key, Summary: value})
out = append(out, orderedDaypartRow{Key: key, Summary: value})
break
}
}
@@ -375,7 +543,7 @@ func orderedTomorrowDayparts(dayparts *map[string]briefing.DerivedDaypartSummary
}
sort.Strings(remaining)
for _, key := range remaining {
out = append(out, TomorrowDaypartContext{Key: key, Summary: (*dayparts)[key]})
out = append(out, orderedDaypartRow{Key: key, Summary: (*dayparts)[key]})
}
return out
}