Replace legacy daily report with generated text daily report
This commit is contained in:
@@ -1,24 +1,27 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
|
||||
)
|
||||
|
||||
func dailyTodayDefinition() Definition {
|
||||
func dailyDefinition() Definition {
|
||||
return Definition{
|
||||
ID: DailyToday,
|
||||
Name: "Daily Report",
|
||||
PromptID: "weather.daily_report",
|
||||
GenerationMode: GenerationModeScriptoriumMarkdown,
|
||||
ComparisonStrategy: CompareSameValidDate,
|
||||
ArtifactGroup: "daily",
|
||||
BatchOutputName: "daily.md",
|
||||
Generated: true,
|
||||
CompatiblePriorIDs: []ID{DailyToday},
|
||||
Modules: dailyTodayModules(),
|
||||
Morning: true,
|
||||
resolve: resolveDailyToday,
|
||||
ID: Daily,
|
||||
Name: "Daily Report",
|
||||
PromptID: "weather.daily_generated_text",
|
||||
GenerationMode: GenerationModeGeneratedTextTemplate,
|
||||
TemplateID: "daily",
|
||||
GeneratedTextSchemaID: "daily",
|
||||
ComparisonStrategy: CompareSameValidDate,
|
||||
ArtifactGroup: "daily",
|
||||
BatchOutputName: "daily.md",
|
||||
Generated: true,
|
||||
CompatiblePriorIDs: []ID{Daily},
|
||||
Modules: dailyModules(),
|
||||
resolve: resolveDaily,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +44,7 @@ func tomorrowDefinition() Definition {
|
||||
}
|
||||
}
|
||||
|
||||
func dailyTodayModules() []module.ConfigItem {
|
||||
func dailyModules() []module.ConfigItem {
|
||||
return moduleItems(
|
||||
module.Metadata,
|
||||
module.CurrentConditions,
|
||||
@@ -55,6 +58,7 @@ func dailyTodayModules() []module.ConfigItem {
|
||||
module.SPCConvectiveDiscussion,
|
||||
module.WeatherStory,
|
||||
module.OutdoorWindows,
|
||||
module.DailyPlanning,
|
||||
module.HourlyForecast,
|
||||
)
|
||||
}
|
||||
@@ -78,11 +82,11 @@ func tomorrowModules() []module.ConfigItem {
|
||||
)
|
||||
}
|
||||
|
||||
func resolveDailyToday(req ResolveRequest) (timeutil.Period, error) {
|
||||
if !req.Date.IsZero() {
|
||||
return timeutil.CivilDay(req.Date, req.Location), nil
|
||||
func resolveDaily(req ResolveRequest) (timeutil.Period, error) {
|
||||
if req.Date.IsZero() {
|
||||
return timeutil.Period{}, fmt.Errorf("daily report requires an explicit date")
|
||||
}
|
||||
return timeutil.CivilDay(req.Now, req.Location), nil
|
||||
return timeutil.CivilDay(req.Date, req.Location), nil
|
||||
}
|
||||
|
||||
func resolveTomorrow(req ResolveRequest) (timeutil.Period, error) {
|
||||
|
||||
@@ -12,13 +12,13 @@ import (
|
||||
type ID string
|
||||
|
||||
const (
|
||||
DailyToday ID = "daily_today"
|
||||
Today ID = "today"
|
||||
Tomorrow ID = "tomorrow"
|
||||
Hourly ID = "hourly"
|
||||
ThreeDay ID = "three_day"
|
||||
Weekend ID = "weekend"
|
||||
Storm ID = "storm"
|
||||
Daily ID = "daily"
|
||||
Today ID = "today"
|
||||
Tomorrow ID = "tomorrow"
|
||||
Hourly ID = "hourly"
|
||||
ThreeDay ID = "three_day"
|
||||
Weekend ID = "weekend"
|
||||
Storm ID = "storm"
|
||||
)
|
||||
|
||||
type ComparisonStrategy string
|
||||
|
||||
@@ -21,7 +21,7 @@ const (
|
||||
func IDForCommandName(name string) (ID, error) {
|
||||
switch name {
|
||||
case CommandNameDaily:
|
||||
return DailyToday, nil
|
||||
return Daily, nil
|
||||
case CommandNameToday:
|
||||
return Today, nil
|
||||
case CommandNameTomorrow:
|
||||
@@ -54,8 +54,8 @@ func CommandNames() []string {
|
||||
func IDForConfigKey(key string) (ID, error) {
|
||||
normalized := strings.ReplaceAll(strings.TrimSpace(strings.ToLower(key)), "-", "_")
|
||||
switch normalized {
|
||||
case "daily", "daily_today":
|
||||
return DailyToday, nil
|
||||
case "daily":
|
||||
return Daily, nil
|
||||
case "today":
|
||||
return Today, nil
|
||||
case "tomorrow":
|
||||
|
||||
@@ -14,11 +14,14 @@ func TestDailyValidPeriod(t *testing.T) {
|
||||
location := mustLoadLocation(t)
|
||||
now := mustParse("2026-05-29T17:45:00-05:00")
|
||||
|
||||
resolved, err := Resolve(DailyToday, ResolveRequest{Now: now, Location: location})
|
||||
if err != nil {
|
||||
t.Fatalf("Resolve() error = %v", err)
|
||||
resolved, err := Resolve(Daily, ResolveRequest{Now: now, Location: location})
|
||||
if err == nil {
|
||||
t.Fatal("Resolve() error = nil, want explicit date requirement")
|
||||
}
|
||||
assertPeriod(t, resolved.ValidPeriod, "2026-05-29T00:00:00-05:00", "2026-05-30T00:00:00-05:00")
|
||||
if !strings.Contains(err.Error(), "requires an explicit date") {
|
||||
t.Fatalf("Resolve() error = %v, want explicit date requirement", err)
|
||||
}
|
||||
_ = resolved
|
||||
}
|
||||
|
||||
func TestDailyValidPeriodCanUseExplicitDate(t *testing.T) {
|
||||
@@ -26,11 +29,23 @@ func TestDailyValidPeriodCanUseExplicitDate(t *testing.T) {
|
||||
now := mustParse("2026-05-29T17:45:00-05:00")
|
||||
date := mustParse("2026-05-31T12:00:00-05:00")
|
||||
|
||||
resolved, err := Resolve(DailyToday, ResolveRequest{Now: now, Location: location, Date: date})
|
||||
resolved, err := Resolve(Daily, ResolveRequest{Now: now, Location: location, Date: date})
|
||||
if err != nil {
|
||||
t.Fatalf("Resolve() error = %v", err)
|
||||
}
|
||||
assertPeriod(t, resolved.ValidPeriod, "2026-05-31T00:00:00-05:00", "2026-06-01T00:00:00-05:00")
|
||||
if resolved.Definition.PromptID != "weather.daily_generated_text" {
|
||||
t.Fatalf("PromptID = %q, want weather.daily_generated_text", resolved.Definition.PromptID)
|
||||
}
|
||||
if resolved.Definition.GenerationMode != GenerationModeGeneratedTextTemplate {
|
||||
t.Fatalf("GenerationMode = %q, want generated_text_template", resolved.Definition.GenerationMode)
|
||||
}
|
||||
if resolved.Definition.TemplateID != "daily" {
|
||||
t.Fatalf("TemplateID = %q, want daily", resolved.Definition.TemplateID)
|
||||
}
|
||||
if resolved.Definition.GeneratedTextSchemaID != "daily" {
|
||||
t.Fatalf("GeneratedTextSchemaID = %q, want daily", resolved.Definition.GeneratedTextSchemaID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTodayValidPeriod(t *testing.T) {
|
||||
@@ -256,7 +271,7 @@ func TestEveningBatchIncludesTomorrow(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBatchesDoNotIncludeHourly(t *testing.T) {
|
||||
func TestBatchesDoNotIncludeManualReports(t *testing.T) {
|
||||
location := mustLoadLocation(t)
|
||||
req := ResolveRequest{Now: mustParse("2026-05-29T06:00:00-05:00"), Location: location}
|
||||
|
||||
@@ -269,8 +284,8 @@ func TestBatchesDoNotIncludeHourly(t *testing.T) {
|
||||
t.Fatalf("BatchReports(evening) error = %v", err)
|
||||
}
|
||||
for _, resolved := range append(morning, evening...) {
|
||||
if resolved.Definition.ID == Hourly {
|
||||
t.Fatalf("batch included %q, want hourly excluded", resolved.Definition.ID)
|
||||
if resolved.Definition.ID == Hourly || resolved.Definition.ID == Daily {
|
||||
t.Fatalf("batch included %q, want manual reports excluded", resolved.Definition.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,7 +295,7 @@ func TestIDForCommandName(t *testing.T) {
|
||||
name string
|
||||
want ID
|
||||
}{
|
||||
{name: "daily", want: DailyToday},
|
||||
{name: "daily", want: Daily},
|
||||
{name: "today", want: Today},
|
||||
{name: "tomorrow", want: Tomorrow},
|
||||
{name: "hourly", want: Hourly},
|
||||
@@ -323,8 +338,7 @@ func TestIDForConfigKey(t *testing.T) {
|
||||
key string
|
||||
want ID
|
||||
}{
|
||||
{key: "daily", want: DailyToday},
|
||||
{key: "daily_today", want: DailyToday},
|
||||
{key: "daily", want: Daily},
|
||||
{key: "today", want: Today},
|
||||
{key: "tomorrow", want: Tomorrow},
|
||||
{key: "hourly", want: Hourly},
|
||||
@@ -351,6 +365,9 @@ func TestIDForConfigKey(t *testing.T) {
|
||||
if _, err := IDForConfigKey("daily_tomorrow"); err == nil || !strings.Contains(err.Error(), `report config key "daily_tomorrow" is not a known report`) {
|
||||
t.Fatalf("IDForConfigKey(daily_tomorrow) error = %v, want unknown key", err)
|
||||
}
|
||||
if _, err := IDForConfigKey("daily_today"); err == nil || !strings.Contains(err.Error(), `report config key "daily_today" is not a known report`) {
|
||||
t.Fatalf("IDForConfigKey(daily_today) error = %v, want unknown key", err)
|
||||
}
|
||||
dailyID, err := IDForConfigKey("daily")
|
||||
if err != nil {
|
||||
t.Fatalf("IDForConfigKey(daily) error = %v", err)
|
||||
@@ -418,7 +435,7 @@ func TestRegistryLookupErrorIsActionable(t *testing.T) {
|
||||
|
||||
func TestRegistryAllIncludesHourlyInStableOrder(t *testing.T) {
|
||||
ids := resolvedDefinitionIDs(DefaultRegistry().All())
|
||||
want := []string{"daily_today", "today", "tomorrow", "hourly", "three_day", "weekend", "storm"}
|
||||
want := []string{"daily", "today", "tomorrow", "hourly", "three_day", "weekend", "storm"}
|
||||
if strings.Join(ids, ",") != strings.Join(want, ",") {
|
||||
t.Fatalf("All() ids = %#v, want %#v", ids, want)
|
||||
}
|
||||
@@ -443,7 +460,7 @@ func TestRegistryDefinitionsDeclareGenerationMetadata(t *testing.T) {
|
||||
if !definition.Generated {
|
||||
continue
|
||||
}
|
||||
if definition.ID == Hourly || definition.ID == Today || definition.ID == Tomorrow {
|
||||
if definition.ID == Hourly || definition.ID == Daily || definition.ID == Today || definition.ID == Tomorrow {
|
||||
wantTemplate := string(definition.ID)
|
||||
if definition.GenerationMode != GenerationModeGeneratedTextTemplate {
|
||||
t.Fatalf("%s GenerationMode = %q, want %q", definition.ID, definition.GenerationMode, GenerationModeGeneratedTextTemplate)
|
||||
@@ -478,11 +495,11 @@ func TestRegistryDefinitionsDeclarePathAndCompatibilityPolicy(t *testing.T) {
|
||||
comparisonStrategy ComparisonStrategy
|
||||
}{
|
||||
{
|
||||
id: DailyToday,
|
||||
id: Daily,
|
||||
artifactGroup: "daily",
|
||||
batchOutputName: "daily.md",
|
||||
generated: true,
|
||||
compatiblePriorIDs: []ID{DailyToday},
|
||||
compatiblePriorIDs: []ID{Daily},
|
||||
comparisonStrategy: CompareSameValidDate,
|
||||
},
|
||||
{
|
||||
@@ -572,7 +589,7 @@ func TestRegistryDefinitionsDeclareDefaultModules(t *testing.T) {
|
||||
want []module.ID
|
||||
}{
|
||||
{
|
||||
id: DailyToday,
|
||||
id: Daily,
|
||||
want: []module.ID{
|
||||
module.Metadata,
|
||||
module.CurrentConditions,
|
||||
@@ -586,6 +603,7 @@ func TestRegistryDefinitionsDeclareDefaultModules(t *testing.T) {
|
||||
module.SPCConvectiveDiscussion,
|
||||
module.WeatherStory,
|
||||
module.OutdoorWindows,
|
||||
module.DailyPlanning,
|
||||
module.HourlyForecast,
|
||||
},
|
||||
},
|
||||
@@ -703,7 +721,7 @@ func TestRegistryDefinitionsDeclareDefaultModules(t *testing.T) {
|
||||
func TestRegistryAppliesModuleOverridesWithoutChangingDefaults(t *testing.T) {
|
||||
base := DefaultRegistry()
|
||||
overridden, err := base.WithModuleOverrides(map[ID][]module.ConfigItem{
|
||||
DailyToday: {
|
||||
Daily: {
|
||||
{ID: module.Metadata},
|
||||
{ID: module.AlertDigest},
|
||||
},
|
||||
@@ -712,7 +730,7 @@ func TestRegistryAppliesModuleOverridesWithoutChangingDefaults(t *testing.T) {
|
||||
t.Fatalf("WithModuleOverrides() error = %v", err)
|
||||
}
|
||||
|
||||
definition, err := overridden.Lookup(DailyToday)
|
||||
definition, err := overridden.Lookup(Daily)
|
||||
if err != nil {
|
||||
t.Fatalf("Lookup(overridden) error = %v", err)
|
||||
}
|
||||
@@ -720,7 +738,7 @@ func TestRegistryAppliesModuleOverridesWithoutChangingDefaults(t *testing.T) {
|
||||
t.Fatalf("overridden ModuleIDs() = %#v, want metadata and alert digest", definition.ModuleIDs())
|
||||
}
|
||||
|
||||
defaultDefinition, err := base.Lookup(DailyToday)
|
||||
defaultDefinition, err := base.Lookup(Daily)
|
||||
if err != nil {
|
||||
t.Fatalf("Lookup(default) error = %v", err)
|
||||
}
|
||||
@@ -743,18 +761,22 @@ func TestRegistryRejectsModuleOverrideForUnknownReport(t *testing.T) {
|
||||
|
||||
func TestResolvedMetadata(t *testing.T) {
|
||||
location := mustLoadLocation(t)
|
||||
resolved, err := Resolve(DailyToday, ResolveRequest{Now: mustParse("2026-05-29T05:00:00-05:00"), Location: location})
|
||||
resolved, err := Resolve(Daily, ResolveRequest{
|
||||
Now: mustParse("2026-05-29T05:00:00-05:00"),
|
||||
Location: location,
|
||||
Date: mustParse("2026-05-29T12:00:00-05:00"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Resolve() error = %v", err)
|
||||
}
|
||||
metadata := resolved.Metadata()
|
||||
if metadata.ReportID != DailyToday {
|
||||
t.Fatalf("ReportID = %q, want daily_today", metadata.ReportID)
|
||||
if metadata.ReportID != Daily {
|
||||
t.Fatalf("ReportID = %q, want daily", metadata.ReportID)
|
||||
}
|
||||
if metadata.PromptID != "weather.daily_report" {
|
||||
t.Fatalf("PromptID = %q, want weather.daily_report", metadata.PromptID)
|
||||
if metadata.PromptID != "weather.daily_generated_text" {
|
||||
t.Fatalf("PromptID = %q, want weather.daily_generated_text", metadata.PromptID)
|
||||
}
|
||||
if !strings.Contains(metadata.RunID, "daily_today") {
|
||||
if !strings.Contains(metadata.RunID, "daily") {
|
||||
t.Fatalf("RunID = %q, want report id", metadata.RunID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ type Registry struct {
|
||||
|
||||
func DefaultRegistry() Registry {
|
||||
definitions := []Definition{
|
||||
dailyTodayDefinition(),
|
||||
dailyDefinition(),
|
||||
todayDefinition(),
|
||||
tomorrowDefinition(),
|
||||
hourlyDefinition(),
|
||||
@@ -75,7 +75,7 @@ func (r Registry) MustLookup(id ID) Definition {
|
||||
}
|
||||
|
||||
func (r Registry) All() []Definition {
|
||||
ids := []ID{DailyToday, Today, Tomorrow, Hourly, ThreeDay, Weekend, Storm}
|
||||
ids := []ID{Daily, Today, Tomorrow, Hourly, ThreeDay, Weekend, Storm}
|
||||
out := make([]Definition, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
if definition, ok := r.definitions[id]; ok {
|
||||
|
||||
Reference in New Issue
Block a user