Share day-style render context and template blocks

This commit is contained in:
2026-06-16 15:03:35 +00:00
parent e3bcecc5c1
commit 121f28fd29
10 changed files with 426 additions and 252 deletions

View File

@@ -165,6 +165,31 @@ type DailyDaypartContext struct {
Summary briefing.DerivedDaypartSummaryModule
}
type dayStyleReportContext struct {
Title string
ForecastDate time.Time
ForecastDateLabel string
ForecastDayName string
GeneratedAt time.Time
GeneratedAtLabel string
ValidPeriod timeutil.Period
Timezone string
}
type dayStyleTemplateModules struct {
Metadata *briefing.MetadataModule
CurrentConditions *briefing.CurrentConditionsModule
HourlyForecast *briefing.HourlyForecastModule
DerivedDailySummary *briefing.DerivedDailySummaryModule
DerivedDaypartSummaries *map[string]briefing.DerivedDaypartSummaryModule
PrecipTiming *briefing.PrecipTimingModule
AlertDigest *briefing.AlertDigestModule
SPCConvectiveOutlooks *briefing.SPCConvectiveOutlooksModule
AreaForecastDiscussion *briefing.AreaForecastDiscussionModule
SPCConvectiveDiscussion *briefing.SPCConvectiveDiscussionModule
WeatherStory *briefing.WeatherStoryModule
}
func BuildHourlyRenderContext(metadata briefing.Metadata, snapshot module.Snapshot, generated Hourly, collected facts.CollectedFacts, derived facts.DerivedFacts) (HourlyRenderContext, error) {
location, err := timeutil.LoadLocation(metadata.Timezone)
if err != nil {
@@ -198,33 +223,18 @@ func BuildHourlyRenderContext(metadata briefing.Metadata, snapshot module.Snapsh
}
func BuildDailyRenderContext(metadata briefing.Metadata, snapshot module.Snapshot, generated Daily, collected facts.CollectedFacts, derived facts.DerivedFacts) (DailyRenderContext, error) {
location, err := timeutil.LoadLocation(metadata.Timezone)
reportContext, err := buildDayStyleReportContext(metadata, "daily", func(dayName string) string {
return dayName + "'s Weather"
})
if err != nil {
return DailyRenderContext{}, fmt.Errorf("build daily render context: %w", err)
}
if metadata.GeneratedAt.IsZero() {
return DailyRenderContext{}, fmt.Errorf("build daily render context: generatedAt is required")
}
if !metadata.ValidPeriod.IsValid() {
return DailyRenderContext{}, fmt.Errorf("build daily render context: valid period is required")
return DailyRenderContext{}, err
}
modules, err := dailyTemplateModules(snapshot, derived)
if err != nil {
return DailyRenderContext{}, err
}
forecastDate := metadata.ValidPeriod.Start.In(location)
forecastDayName := forecastDate.Format("Monday")
return DailyRenderContext{
Report: DailyReportContext{
Title: forecastDayName + "'s Weather",
ForecastDate: forecastDate,
ForecastDateLabel: forecastDate.Format("Monday, January 2, 2006"),
ForecastDayName: forecastDayName,
GeneratedAt: metadata.GeneratedAt,
GeneratedAtLabel: generatedAtLabel(metadata.GeneratedAt, location),
ValidPeriod: metadata.ValidPeriod,
Timezone: metadata.Timezone,
},
Report: reportContext.dailyReportContext(),
GeneratedText: generated,
Modules: modules,
Collected: collected,
@@ -233,32 +243,18 @@ func BuildDailyRenderContext(metadata briefing.Metadata, snapshot module.Snapsho
}
func BuildTodayRenderContext(metadata briefing.Metadata, snapshot module.Snapshot, generated Today, collected facts.CollectedFacts, derived facts.DerivedFacts) (TodayRenderContext, error) {
location, err := timeutil.LoadLocation(metadata.Timezone)
reportContext, err := buildDayStyleReportContext(metadata, "today", func(string) string {
return "Today's Weather"
})
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")
return TodayRenderContext{}, err
}
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,
},
Report: reportContext.todayReportContext(),
GeneratedText: generated,
Modules: modules,
Collected: collected,
@@ -267,33 +263,18 @@ func BuildTodayRenderContext(metadata briefing.Metadata, snapshot module.Snapsho
}
func BuildTomorrowRenderContext(metadata briefing.Metadata, snapshot module.Snapshot, generated Tomorrow, collected facts.CollectedFacts, derived facts.DerivedFacts) (TomorrowRenderContext, error) {
location, err := timeutil.LoadLocation(metadata.Timezone)
reportContext, err := buildDayStyleReportContext(metadata, "tomorrow", func(dayName string) string {
return dayName + "'s Weather"
})
if err != nil {
return TomorrowRenderContext{}, fmt.Errorf("build tomorrow render context: %w", err)
}
if metadata.GeneratedAt.IsZero() {
return TomorrowRenderContext{}, fmt.Errorf("build tomorrow render context: generatedAt is required")
}
if !metadata.ValidPeriod.IsValid() {
return TomorrowRenderContext{}, fmt.Errorf("build tomorrow render context: valid period is required")
return TomorrowRenderContext{}, err
}
modules, err := tomorrowTemplateModules(snapshot, derived)
if err != nil {
return TomorrowRenderContext{}, err
}
forecastDate := metadata.ValidPeriod.Start.In(location)
forecastDayName := forecastDate.Format("Monday")
return TomorrowRenderContext{
Report: TomorrowReportContext{
Title: forecastDayName + "'s Weather",
ForecastDate: forecastDate,
ForecastDateLabel: forecastDate.Format("Monday, January 2, 2006"),
ForecastDayName: forecastDayName,
GeneratedAt: metadata.GeneratedAt,
GeneratedAtLabel: generatedAtLabel(metadata.GeneratedAt, location),
ValidPeriod: metadata.ValidPeriod,
Timezone: metadata.Timezone,
},
Report: reportContext.tomorrowReportContext(),
GeneratedText: generated,
Modules: modules,
Collected: collected,
@@ -301,49 +282,73 @@ func BuildTomorrowRenderContext(metadata briefing.Metadata, snapshot module.Snap
}, nil
}
func buildDayStyleReportContext(metadata briefing.Metadata, name string, title func(string) string) (dayStyleReportContext, error) {
location, err := timeutil.LoadLocation(metadata.Timezone)
if err != nil {
return dayStyleReportContext{}, fmt.Errorf("build %s render context: %w", name, err)
}
if metadata.GeneratedAt.IsZero() {
return dayStyleReportContext{}, fmt.Errorf("build %s render context: generatedAt is required", name)
}
if !metadata.ValidPeriod.IsValid() {
return dayStyleReportContext{}, fmt.Errorf("build %s render context: valid period is required", name)
}
forecastDate := metadata.ValidPeriod.Start.In(location)
forecastDayName := forecastDate.Format("Monday")
return dayStyleReportContext{
Title: title(forecastDayName),
ForecastDate: forecastDate,
ForecastDateLabel: forecastDate.Format("Monday, January 2, 2006"),
ForecastDayName: forecastDayName,
GeneratedAt: metadata.GeneratedAt,
GeneratedAtLabel: generatedAtLabel(metadata.GeneratedAt, location),
ValidPeriod: metadata.ValidPeriod,
Timezone: metadata.Timezone,
}, nil
}
func (ctx dayStyleReportContext) dailyReportContext() DailyReportContext {
return DailyReportContext{
Title: ctx.Title,
ForecastDate: ctx.ForecastDate,
ForecastDateLabel: ctx.ForecastDateLabel,
ForecastDayName: ctx.ForecastDayName,
GeneratedAt: ctx.GeneratedAt,
GeneratedAtLabel: ctx.GeneratedAtLabel,
ValidPeriod: ctx.ValidPeriod,
Timezone: ctx.Timezone,
}
}
func (ctx dayStyleReportContext) todayReportContext() TodayReportContext {
return TodayReportContext{
Title: ctx.Title,
ForecastDate: ctx.ForecastDate,
ForecastDateLabel: ctx.ForecastDateLabel,
ForecastDayName: ctx.ForecastDayName,
GeneratedAt: ctx.GeneratedAt,
GeneratedAtLabel: ctx.GeneratedAtLabel,
ValidPeriod: ctx.ValidPeriod,
Timezone: ctx.Timezone,
}
}
func (ctx dayStyleReportContext) tomorrowReportContext() TomorrowReportContext {
return TomorrowReportContext{
Title: ctx.Title,
ForecastDate: ctx.ForecastDate,
ForecastDateLabel: ctx.ForecastDateLabel,
ForecastDayName: ctx.ForecastDayName,
GeneratedAt: ctx.GeneratedAt,
GeneratedAtLabel: ctx.GeneratedAtLabel,
ValidPeriod: ctx.ValidPeriod,
Timezone: ctx.Timezone,
}
}
func dailyTemplateModules(snapshot module.Snapshot, derived facts.DerivedFacts) (DailyTemplateModules, error) {
lookup := newModuleSnapshotLookup(snapshot)
metadata, err := lookup.metadata()
if err != nil {
return DailyTemplateModules{}, err
}
current, err := lookup.currentConditions()
if err != nil {
return DailyTemplateModules{}, err
}
hourly, err := lookup.hourlyForecast()
if err != nil {
return DailyTemplateModules{}, err
}
daily, err := lookup.derivedDailySummary()
if err != nil {
return DailyTemplateModules{}, err
}
dayparts, err := lookup.derivedDaypartSummaries()
if err != nil {
return DailyTemplateModules{}, err
}
precip, err := lookup.precipTiming()
if err != nil {
return DailyTemplateModules{}, err
}
alerts, err := lookup.alertDigest()
if err != nil {
return DailyTemplateModules{}, err
}
outlooks, err := lookup.spcConvectiveOutlooks()
if err != nil {
return DailyTemplateModules{}, err
}
discussion, err := lookup.areaForecastDiscussion()
if err != nil {
return DailyTemplateModules{}, err
}
spcDiscussion, err := lookup.spcConvectiveDiscussion()
if err != nil {
return DailyTemplateModules{}, err
}
story, err := lookup.weatherStory()
common, lookup, err := dayStyleTemplateModuleSnapshot(snapshot)
if err != nil {
return DailyTemplateModules{}, err
}
@@ -356,66 +361,25 @@ func dailyTemplateModules(snapshot module.Snapshot, derived facts.DerivedFacts)
return DailyTemplateModules{}, err
}
return DailyTemplateModules{
Metadata: metadata,
CurrentConditions: current,
HourlyForecast: hourly,
DerivedDailySummary: daily,
DerivedDaypartSummaries: dayparts,
Dayparts: orderedDailyDayparts(dayparts, derived.DaypartSummaries),
PrecipTiming: precip,
AlertDigest: alerts,
SPCConvectiveOutlooks: outlooks,
AreaForecastDiscussion: discussion,
SPCConvectiveDiscussion: spcDiscussion,
WeatherStory: story,
Metadata: common.Metadata,
CurrentConditions: common.CurrentConditions,
HourlyForecast: common.HourlyForecast,
DerivedDailySummary: common.DerivedDailySummary,
DerivedDaypartSummaries: common.DerivedDaypartSummaries,
Dayparts: orderedDailyDayparts(common.DerivedDaypartSummaries, derived.DaypartSummaries),
PrecipTiming: common.PrecipTiming,
AlertDigest: common.AlertDigest,
SPCConvectiveOutlooks: common.SPCConvectiveOutlooks,
AreaForecastDiscussion: common.AreaForecastDiscussion,
SPCConvectiveDiscussion: common.SPCConvectiveDiscussion,
WeatherStory: common.WeatherStory,
OutdoorWindows: outdoor,
DailyPlanning: planning,
}, 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()
common, lookup, err := dayStyleTemplateModuleSnapshot(snapshot)
if err != nil {
return TodayTemplateModules{}, err
}
@@ -424,18 +388,18 @@ func todayTemplateModules(snapshot module.Snapshot, derived facts.DerivedFacts)
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,
Metadata: common.Metadata,
CurrentConditions: common.CurrentConditions,
HourlyForecast: common.HourlyForecast,
DerivedDailySummary: common.DerivedDailySummary,
DerivedDaypartSummaries: common.DerivedDaypartSummaries,
Dayparts: orderedTodayDayparts(common.DerivedDaypartSummaries, derived.DaypartSummaries),
PrecipTiming: common.PrecipTiming,
AlertDigest: common.AlertDigest,
SPCConvectiveOutlooks: common.SPCConvectiveOutlooks,
AreaForecastDiscussion: common.AreaForecastDiscussion,
SPCConvectiveDiscussion: common.SPCConvectiveDiscussion,
WeatherStory: common.WeatherStory,
TodayPlanning: planning,
}, nil
}
@@ -492,48 +456,7 @@ func hourlyTemplateModules(snapshot module.Snapshot) (HourlyTemplateModules, err
}
func tomorrowTemplateModules(snapshot module.Snapshot, derived facts.DerivedFacts) (TomorrowTemplateModules, error) {
lookup := newModuleSnapshotLookup(snapshot)
metadata, err := lookup.metadata()
if err != nil {
return TomorrowTemplateModules{}, err
}
current, err := lookup.currentConditions()
if err != nil {
return TomorrowTemplateModules{}, err
}
hourly, err := lookup.hourlyForecast()
if err != nil {
return TomorrowTemplateModules{}, err
}
daily, err := lookup.derivedDailySummary()
if err != nil {
return TomorrowTemplateModules{}, err
}
dayparts, err := lookup.derivedDaypartSummaries()
if err != nil {
return TomorrowTemplateModules{}, err
}
precip, err := lookup.precipTiming()
if err != nil {
return TomorrowTemplateModules{}, err
}
alerts, err := lookup.alertDigest()
if err != nil {
return TomorrowTemplateModules{}, err
}
outlooks, err := lookup.spcConvectiveOutlooks()
if err != nil {
return TomorrowTemplateModules{}, err
}
discussion, err := lookup.areaForecastDiscussion()
if err != nil {
return TomorrowTemplateModules{}, err
}
spcDiscussion, err := lookup.spcConvectiveDiscussion()
if err != nil {
return TomorrowTemplateModules{}, err
}
story, err := lookup.weatherStory()
common, lookup, err := dayStyleTemplateModuleSnapshot(snapshot)
if err != nil {
return TomorrowTemplateModules{}, err
}
@@ -542,20 +465,81 @@ func tomorrowTemplateModules(snapshot module.Snapshot, derived facts.DerivedFact
return TomorrowTemplateModules{}, err
}
return TomorrowTemplateModules{
Metadata: common.Metadata,
CurrentConditions: common.CurrentConditions,
HourlyForecast: common.HourlyForecast,
DerivedDailySummary: common.DerivedDailySummary,
DerivedDaypartSummaries: common.DerivedDaypartSummaries,
Dayparts: orderedTomorrowDayparts(common.DerivedDaypartSummaries, derived.DaypartSummaries),
PrecipTiming: common.PrecipTiming,
AlertDigest: common.AlertDigest,
SPCConvectiveOutlooks: common.SPCConvectiveOutlooks,
AreaForecastDiscussion: common.AreaForecastDiscussion,
SPCConvectiveDiscussion: common.SPCConvectiveDiscussion,
WeatherStory: common.WeatherStory,
TomorrowPlanning: planning,
}, nil
}
func dayStyleTemplateModuleSnapshot(snapshot module.Snapshot) (dayStyleTemplateModules, moduleSnapshotLookup, error) {
lookup := newModuleSnapshotLookup(snapshot)
metadata, err := lookup.metadata()
if err != nil {
return dayStyleTemplateModules{}, moduleSnapshotLookup{}, err
}
current, err := lookup.currentConditions()
if err != nil {
return dayStyleTemplateModules{}, moduleSnapshotLookup{}, err
}
hourly, err := lookup.hourlyForecast()
if err != nil {
return dayStyleTemplateModules{}, moduleSnapshotLookup{}, err
}
daily, err := lookup.derivedDailySummary()
if err != nil {
return dayStyleTemplateModules{}, moduleSnapshotLookup{}, err
}
dayparts, err := lookup.derivedDaypartSummaries()
if err != nil {
return dayStyleTemplateModules{}, moduleSnapshotLookup{}, err
}
precip, err := lookup.precipTiming()
if err != nil {
return dayStyleTemplateModules{}, moduleSnapshotLookup{}, err
}
alerts, err := lookup.alertDigest()
if err != nil {
return dayStyleTemplateModules{}, moduleSnapshotLookup{}, err
}
outlooks, err := lookup.spcConvectiveOutlooks()
if err != nil {
return dayStyleTemplateModules{}, moduleSnapshotLookup{}, err
}
discussion, err := lookup.areaForecastDiscussion()
if err != nil {
return dayStyleTemplateModules{}, moduleSnapshotLookup{}, err
}
spcDiscussion, err := lookup.spcConvectiveDiscussion()
if err != nil {
return dayStyleTemplateModules{}, moduleSnapshotLookup{}, err
}
story, err := lookup.weatherStory()
if err != nil {
return dayStyleTemplateModules{}, moduleSnapshotLookup{}, err
}
return dayStyleTemplateModules{
Metadata: metadata,
CurrentConditions: current,
HourlyForecast: hourly,
DerivedDailySummary: daily,
DerivedDaypartSummaries: dayparts,
Dayparts: orderedTomorrowDayparts(dayparts, derived.DaypartSummaries),
PrecipTiming: precip,
AlertDigest: alerts,
SPCConvectiveOutlooks: outlooks,
AreaForecastDiscussion: discussion,
SPCConvectiveDiscussion: spcDiscussion,
WeatherStory: story,
TomorrowPlanning: planning,
}, nil
}, lookup, nil
}
type moduleSnapshotLookup struct {