Implement source-oriented briefing modules

This commit is contained in:
2026-06-09 20:35:57 +00:00
parent 24dba3bd60
commit 9501dad1dc
4 changed files with 571 additions and 0 deletions

View File

@@ -4,10 +4,22 @@ import (
"fmt"
"reflect"
"gitea.maximumdirect.net/eric/weatherreporter/internal/facts"
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
)
type ModuleContext struct {
Resolved report.Resolved
Collected facts.CollectedFacts
Derived facts.DerivedFacts
Units string
Timezone string
Location *LocationContext
}
type ModuleBuilder func(ModuleContext, any) (*module.Output, error)
type ModuleDefinition struct {
ID module.ID
StanzaName string
@@ -17,6 +29,7 @@ type ModuleDefinition struct {
SupportedReports []report.ID
MissingData module.MissingDataBehavior
AllowDuplicate bool
Builder ModuleBuilder
}
type ModuleRegistry struct {
@@ -65,6 +78,40 @@ func (r ModuleRegistry) Lookup(id module.ID) (ModuleDefinition, error) {
return definition, nil
}
func (r ModuleRegistry) BuildModule(ctx ModuleContext, item module.ConfigItem) (*module.Output, error) {
definition, err := r.Lookup(item.ID)
if err != nil {
return nil, err
}
if !definition.SupportsReport(ctx.Resolved.Definition.ID) {
return nil, fmt.Errorf("module %q is not compatible with report %q", item.ID, ctx.Resolved.Definition.ID)
}
if err := definition.ValidateOptions(item.Options); err != nil {
return nil, err
}
if definition.Builder == nil {
return nil, fmt.Errorf("module %q has no builder", item.ID)
}
options := item.Options
if options == nil {
options = definition.DefaultOptions
}
output, err := definition.Builder(ctx, options)
if err != nil {
return nil, err
}
if output == nil {
return nil, nil
}
if output.ID != definition.ID {
return nil, fmt.Errorf("module %q produced output id %q", definition.ID, output.ID)
}
if output.StanzaName != definition.StanzaName {
return nil, fmt.Errorf("module %q produced stanza %q, want %q", definition.ID, output.StanzaName, definition.StanzaName)
}
return output, nil
}
func (r ModuleRegistry) ValidateComposition(reportID report.ID, items []module.ConfigItem) error {
seenModules := map[module.ID]struct{}{}
seenStanzas := map[string]module.ID{}
@@ -132,6 +179,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
RequiredCollected: []module.FactRequirement{module.CollectedSourceMetadata},
SupportedReports: allReports,
MissingData: module.MissingDataEmpty,
Builder: buildMetadataModule,
},
{
ID: module.CurrentConditions,
@@ -140,6 +188,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
RequiredCollected: []module.FactRequirement{module.CollectedCurrentConditions},
SupportedReports: allReports,
MissingData: module.MissingDataOmit,
Builder: buildCurrentConditionsModule,
},
{
ID: module.DerivedDailySummary,
@@ -181,6 +230,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
RequiredDerived: []module.FactRequirement{module.RequiresDerivedAlertOverlaps},
SupportedReports: allReports,
MissingData: module.MissingDataEmpty,
Builder: buildAlertDigestModule,
},
{
ID: module.AreaForecastDiscussion,
@@ -189,6 +239,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
RequiredCollected: []module.FactRequirement{module.CollectedDiscussion},
SupportedReports: allReports,
MissingData: module.MissingDataOmit,
Builder: buildAreaForecastDiscussionModule,
},
{
ID: module.WeatherStory,
@@ -197,6 +248,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
RequiredCollected: []module.FactRequirement{module.CollectedWeatherStory},
SupportedReports: allReports,
MissingData: module.MissingDataOmit,
Builder: buildWeatherStoryModule,
},
{
ID: module.ForecastDelta,