Validate render identity and daypart fallbacks
This commit is contained in:
@@ -2,6 +2,7 @@ package generatedtext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/facts"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
|
||||
)
|
||||
|
||||
@@ -16,8 +18,6 @@ type HourlyRenderContext struct {
|
||||
Report HourlyReportContext
|
||||
GeneratedText Hourly
|
||||
Modules HourlyTemplateModules
|
||||
Collected facts.CollectedFacts
|
||||
Derived facts.DerivedFacts
|
||||
}
|
||||
|
||||
type HourlyReportContext struct {
|
||||
@@ -31,7 +31,6 @@ type HourlyReportContext struct {
|
||||
}
|
||||
|
||||
type HourlyTemplateModules struct {
|
||||
Metadata *briefing.MetadataModule
|
||||
CurrentConditions *briefing.CurrentConditionsModule
|
||||
HourlyForecast *briefing.HourlyForecastModule
|
||||
PrecipTiming *briefing.PrecipTimingModule
|
||||
@@ -46,24 +45,18 @@ type TomorrowRenderContext struct {
|
||||
Report TomorrowReportContext
|
||||
GeneratedText Tomorrow
|
||||
Modules TomorrowTemplateModules
|
||||
Collected facts.CollectedFacts
|
||||
Derived facts.DerivedFacts
|
||||
}
|
||||
|
||||
type DailyRenderContext struct {
|
||||
Report DailyReportContext
|
||||
GeneratedText Daily
|
||||
Modules DailyTemplateModules
|
||||
Collected facts.CollectedFacts
|
||||
Derived facts.DerivedFacts
|
||||
}
|
||||
|
||||
type TodayRenderContext struct {
|
||||
Report TodayReportContext
|
||||
GeneratedText Today
|
||||
Modules TodayTemplateModules
|
||||
Collected facts.CollectedFacts
|
||||
Derived facts.DerivedFacts
|
||||
}
|
||||
|
||||
type TodayReportContext struct {
|
||||
@@ -78,12 +71,12 @@ type TodayReportContext struct {
|
||||
}
|
||||
|
||||
type TodayTemplateModules struct {
|
||||
Metadata *briefing.MetadataModule
|
||||
CurrentConditions *briefing.CurrentConditionsModule
|
||||
HourlyForecast *briefing.HourlyForecastModule
|
||||
DerivedDailySummary *briefing.DerivedDailySummaryModule
|
||||
DerivedDaypartSummaries *map[string]briefing.DerivedDaypartSummaryModule
|
||||
Dayparts []TodayDaypartContext
|
||||
HasDaypartDetails bool
|
||||
PrecipTiming *briefing.PrecipTimingModule
|
||||
AlertDigest *briefing.AlertDigestModule
|
||||
SPCConvectiveOutlooks *briefing.SPCConvectiveOutlooksModule
|
||||
@@ -110,7 +103,6 @@ type TomorrowReportContext struct {
|
||||
}
|
||||
|
||||
type TomorrowTemplateModules struct {
|
||||
Metadata *briefing.MetadataModule
|
||||
CurrentConditions *briefing.CurrentConditionsModule
|
||||
HourlyForecast *briefing.HourlyForecastModule
|
||||
DerivedDailySummary *briefing.DerivedDailySummaryModule
|
||||
@@ -142,7 +134,6 @@ type DailyReportContext struct {
|
||||
}
|
||||
|
||||
type DailyTemplateModules struct {
|
||||
Metadata *briefing.MetadataModule
|
||||
CurrentConditions *briefing.CurrentConditionsModule
|
||||
HourlyForecast *briefing.HourlyForecastModule
|
||||
DerivedDailySummary *briefing.DerivedDailySummaryModule
|
||||
@@ -175,7 +166,6 @@ type dayStyleReportContext struct {
|
||||
}
|
||||
|
||||
type dayStyleTemplateModules struct {
|
||||
Metadata *briefing.MetadataModule
|
||||
CurrentConditions *briefing.CurrentConditionsModule
|
||||
HourlyForecast *briefing.HourlyForecastModule
|
||||
DerivedDailySummary *briefing.DerivedDailySummaryModule
|
||||
@@ -188,7 +178,10 @@ type dayStyleTemplateModules struct {
|
||||
WeatherStory *briefing.WeatherStoryModule
|
||||
}
|
||||
|
||||
func BuildHourlyRenderContext(identity briefing.PreparedIdentity, snapshot module.Snapshot, generated Hourly, collected facts.CollectedFacts, derived facts.DerivedFacts) (HourlyRenderContext, error) {
|
||||
func BuildHourlyRenderContext(identity briefing.PreparedIdentity, snapshot module.Snapshot, generated Hourly) (HourlyRenderContext, error) {
|
||||
if err := validateRenderIdentity(report.Hourly, identity, snapshot); err != nil {
|
||||
return HourlyRenderContext{}, err
|
||||
}
|
||||
location, err := timeutil.LoadLocation(identity.Timezone)
|
||||
if err != nil {
|
||||
return HourlyRenderContext{}, fmt.Errorf("build hourly render context: %w", err)
|
||||
@@ -215,12 +208,13 @@ func BuildHourlyRenderContext(identity briefing.PreparedIdentity, snapshot modul
|
||||
},
|
||||
GeneratedText: generated,
|
||||
Modules: modules,
|
||||
Collected: collected,
|
||||
Derived: derived,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func BuildDailyRenderContext(identity briefing.PreparedIdentity, snapshot module.Snapshot, generated Daily, collected facts.CollectedFacts, derived facts.DerivedFacts) (DailyRenderContext, error) {
|
||||
func BuildDailyRenderContext(identity briefing.PreparedIdentity, snapshot module.Snapshot, generated Daily, derived facts.DerivedFacts) (DailyRenderContext, error) {
|
||||
if err := validateRenderIdentity(report.Daily, identity, snapshot); err != nil {
|
||||
return DailyRenderContext{}, err
|
||||
}
|
||||
reportContext, err := buildDayStyleReportContext(identity, "daily", func(dayName string) string {
|
||||
return dayName + "'s Weather"
|
||||
})
|
||||
@@ -235,12 +229,13 @@ func BuildDailyRenderContext(identity briefing.PreparedIdentity, snapshot module
|
||||
Report: reportContext.dailyReportContext(),
|
||||
GeneratedText: generated,
|
||||
Modules: modules,
|
||||
Collected: collected,
|
||||
Derived: derived,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func BuildTodayRenderContext(identity briefing.PreparedIdentity, snapshot module.Snapshot, generated Today, collected facts.CollectedFacts, derived facts.DerivedFacts) (TodayRenderContext, error) {
|
||||
func BuildTodayRenderContext(identity briefing.PreparedIdentity, snapshot module.Snapshot, generated Today, derived facts.DerivedFacts) (TodayRenderContext, error) {
|
||||
if err := validateRenderIdentity(report.Today, identity, snapshot); err != nil {
|
||||
return TodayRenderContext{}, err
|
||||
}
|
||||
reportContext, err := buildDayStyleReportContext(identity, "today", func(string) string {
|
||||
return "Today's Weather"
|
||||
})
|
||||
@@ -255,12 +250,13 @@ func BuildTodayRenderContext(identity briefing.PreparedIdentity, snapshot module
|
||||
Report: reportContext.todayReportContext(),
|
||||
GeneratedText: generated,
|
||||
Modules: modules,
|
||||
Collected: collected,
|
||||
Derived: derived,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func BuildTomorrowRenderContext(identity briefing.PreparedIdentity, snapshot module.Snapshot, generated Tomorrow, collected facts.CollectedFacts, derived facts.DerivedFacts) (TomorrowRenderContext, error) {
|
||||
func BuildTomorrowRenderContext(identity briefing.PreparedIdentity, snapshot module.Snapshot, generated Tomorrow, derived facts.DerivedFacts) (TomorrowRenderContext, error) {
|
||||
if err := validateRenderIdentity(report.Tomorrow, identity, snapshot); err != nil {
|
||||
return TomorrowRenderContext{}, err
|
||||
}
|
||||
reportContext, err := buildDayStyleReportContext(identity, "tomorrow", func(dayName string) string {
|
||||
return dayName + "'s Weather"
|
||||
})
|
||||
@@ -275,11 +271,36 @@ func BuildTomorrowRenderContext(identity briefing.PreparedIdentity, snapshot mod
|
||||
Report: reportContext.tomorrowReportContext(),
|
||||
GeneratedText: generated,
|
||||
Modules: modules,
|
||||
Collected: collected,
|
||||
Derived: derived,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func validateRenderIdentity(expected report.ID, identity briefing.PreparedIdentity, snapshot module.Snapshot) error {
|
||||
if identity.ReportID != expected {
|
||||
return fmt.Errorf("render identity report %q does not match expected report %q", identity.ReportID, expected)
|
||||
}
|
||||
metadata, found, err := module.StanzaValue[briefing.MetadataModule](snapshot, string(module.Metadata))
|
||||
if err != nil {
|
||||
return fmt.Errorf("read metadata module for report %q: %w", expected, err)
|
||||
}
|
||||
if !found || metadataMatchesIdentity(metadata, identity) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("metadata module does not match render identity for report %q", expected)
|
||||
}
|
||||
|
||||
func metadataMatchesIdentity(metadata briefing.MetadataModule, identity briefing.PreparedIdentity) bool {
|
||||
if metadata.RunID != identity.RunID || metadata.ReportID != identity.ReportID || metadata.Variant != identity.Variant || metadata.PromptID != identity.PromptID || !metadata.GeneratedAt.Equal(identity.GeneratedAt) || metadata.Units != identity.Units || metadata.Timezone != identity.Timezone || metadata.ValidPeriod != identity.ValidPeriod || !reflect.DeepEqual(metadata.Location, identity.Location) || len(metadata.SourceWarnings) != len(identity.SourceWarnings) {
|
||||
return false
|
||||
}
|
||||
for index, warning := range identity.SourceWarnings {
|
||||
summary := metadata.SourceWarnings[index]
|
||||
if summary.Source != warning.Source || summary.Code != warning.Code || summary.Severity != warning.Severity || summary.Message != warning.Message || summary.CompletenessImpact != warning.CompletenessImpact {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func buildDayStyleReportContext(identity briefing.PreparedIdentity, name string, title func(string) string) (dayStyleReportContext, error) {
|
||||
location, err := timeutil.LoadLocation(identity.Timezone)
|
||||
if err != nil {
|
||||
@@ -359,7 +380,6 @@ func dailyTemplateModules(snapshot module.Snapshot, derived facts.DerivedFacts)
|
||||
return DailyTemplateModules{}, err
|
||||
}
|
||||
return DailyTemplateModules{
|
||||
Metadata: common.Metadata,
|
||||
CurrentConditions: common.CurrentConditions,
|
||||
HourlyForecast: common.HourlyForecast,
|
||||
DerivedDailySummary: common.DerivedDailySummary,
|
||||
@@ -385,13 +405,14 @@ func todayTemplateModules(snapshot module.Snapshot, derived facts.DerivedFacts)
|
||||
if err != nil {
|
||||
return TodayTemplateModules{}, err
|
||||
}
|
||||
dayparts := orderedTodayDayparts(common.DerivedDaypartSummaries, derived.DaypartSummaries)
|
||||
return TodayTemplateModules{
|
||||
Metadata: common.Metadata,
|
||||
CurrentConditions: common.CurrentConditions,
|
||||
HourlyForecast: common.HourlyForecast,
|
||||
DerivedDailySummary: common.DerivedDailySummary,
|
||||
DerivedDaypartSummaries: common.DerivedDaypartSummaries,
|
||||
Dayparts: orderedTodayDayparts(common.DerivedDaypartSummaries, derived.DaypartSummaries),
|
||||
Dayparts: dayparts,
|
||||
HasDaypartDetails: hasTodayDaypartDetails(dayparts),
|
||||
PrecipTiming: common.PrecipTiming,
|
||||
AlertDigest: common.AlertDigest,
|
||||
SPCConvectiveOutlooks: common.SPCConvectiveOutlooks,
|
||||
@@ -404,10 +425,6 @@ func todayTemplateModules(snapshot module.Snapshot, derived facts.DerivedFacts)
|
||||
|
||||
func hourlyTemplateModules(snapshot module.Snapshot) (HourlyTemplateModules, error) {
|
||||
lookup := newModuleSnapshotLookup(snapshot)
|
||||
metadata, err := lookup.metadata()
|
||||
if err != nil {
|
||||
return HourlyTemplateModules{}, err
|
||||
}
|
||||
current, err := lookup.currentConditions()
|
||||
if err != nil {
|
||||
return HourlyTemplateModules{}, err
|
||||
@@ -441,7 +458,6 @@ func hourlyTemplateModules(snapshot module.Snapshot) (HourlyTemplateModules, err
|
||||
return HourlyTemplateModules{}, err
|
||||
}
|
||||
return HourlyTemplateModules{
|
||||
Metadata: metadata,
|
||||
CurrentConditions: current,
|
||||
HourlyForecast: hourly,
|
||||
PrecipTiming: precip,
|
||||
@@ -463,7 +479,6 @@ 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,
|
||||
@@ -481,10 +496,6 @@ func tomorrowTemplateModules(snapshot module.Snapshot, derived facts.DerivedFact
|
||||
|
||||
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
|
||||
@@ -526,7 +537,6 @@ func dayStyleTemplateModuleSnapshot(snapshot module.Snapshot) (dayStyleTemplateM
|
||||
return dayStyleTemplateModules{}, moduleSnapshotLookup{}, err
|
||||
}
|
||||
return dayStyleTemplateModules{
|
||||
Metadata: metadata,
|
||||
CurrentConditions: current,
|
||||
HourlyForecast: hourly,
|
||||
DerivedDailySummary: daily,
|
||||
@@ -556,10 +566,6 @@ func newModuleSnapshotLookup(snapshot module.Snapshot) moduleSnapshotLookup {
|
||||
}
|
||||
}
|
||||
|
||||
func (lookup moduleSnapshotLookup) metadata() (*briefing.MetadataModule, error) {
|
||||
return optionalStanza[briefing.MetadataModule](lookup, module.Metadata)
|
||||
}
|
||||
|
||||
func (lookup moduleSnapshotLookup) currentConditions() (*briefing.CurrentConditionsModule, error) {
|
||||
return optionalStanza[briefing.CurrentConditionsModule](lookup, module.CurrentConditions)
|
||||
}
|
||||
@@ -638,6 +644,15 @@ func orderedTodayDayparts(dayparts *map[string]briefing.DerivedDaypartSummaryMod
|
||||
return out
|
||||
}
|
||||
|
||||
func hasTodayDaypartDetails(dayparts []TodayDaypartContext) bool {
|
||||
for _, daypart := range dayparts {
|
||||
if daypart.Summary.DominantConditionDisplay != "" || daypart.Summary.DominantCondition != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func orderedDailyDayparts(dayparts *map[string]briefing.DerivedDaypartSummaryModule, ordered []forecast.DaypartSummary) []DailyDaypartContext {
|
||||
orderedRows := orderedDaypartRows(dayparts, ordered)
|
||||
out := make([]DailyDaypartContext, 0, len(orderedRows))
|
||||
|
||||
Reference in New Issue
Block a user