// Package promptinput builds prompt data packages from briefing packages. package promptinput import ( "fmt" "time" "gitea.maximumdirect.net/eric/weatherreporter/internal/briefing" "gitea.maximumdirect.net/eric/weatherreporter/internal/changes" "gitea.maximumdirect.net/eric/weatherreporter/internal/fileutil" "gitea.maximumdirect.net/eric/weatherreporter/internal/report" "gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil" "gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata" ) const SchemaVersion = "weatherreporter.data_package.v1" type Package struct { SchemaVersion string `json:"schemaVersion"` RunID string `json:"runId"` Report Report `json:"report"` Briefing briefing.Package `json:"briefing"` RecentChanges RecentChanges `json:"recentChanges"` SourceWarnings []weatherdata.SourceWarning `json:"sourceWarnings,omitempty"` } type Report struct { ID report.ID `json:"id"` Variant string `json:"variant,omitempty"` PromptID string `json:"promptId"` GeneratedAt time.Time `json:"generatedAt"` Timezone string `json:"timezone"` CurrentLocalDate string `json:"currentLocalDate"` ValidPeriod timeutil.Period `json:"validPeriod"` } type RecentChanges struct { Items []changes.Change `json:"items"` } func Build(briefingPackage briefing.Package) (Package, error) { return BuildWithRecentChanges(briefingPackage, nil) } func BuildWithRecentChanges(briefingPackage briefing.Package, recentChanges []changes.Change) (Package, error) { localDate, err := currentLocalDate(briefingPackage.Metadata.GeneratedAt, briefingPackage.Metadata.Timezone) if err != nil { return Package{}, err } items := make([]changes.Change, len(recentChanges)) copy(items, recentChanges) if items == nil { items = []changes.Change{} } pkg := Package{ SchemaVersion: SchemaVersion, RunID: briefingPackage.Metadata.RunID, Report: Report{ ID: briefingPackage.Metadata.ReportID, Variant: briefingPackage.Metadata.Variant, PromptID: briefingPackage.Metadata.PromptID, GeneratedAt: briefingPackage.Metadata.GeneratedAt, Timezone: briefingPackage.Metadata.Timezone, CurrentLocalDate: localDate, ValidPeriod: briefingPackage.Metadata.ValidPeriod, }, Briefing: briefingPackage, RecentChanges: RecentChanges{Items: items}, SourceWarnings: briefingPackage.Metadata.SourceWarnings, } if err := Validate(pkg); err != nil { return Package{}, err } return pkg, nil } func currentLocalDate(generatedAt time.Time, timezone string) (string, error) { location, err := timeutil.LoadLocation(timezone) if err != nil { return "", fmt.Errorf("load report timezone %q: %w", timezone, err) } return generatedAt.In(location).Format(timeutil.DateLayout), nil } func Validate(pkg Package) error { if pkg.SchemaVersion == "" { return fmt.Errorf("schemaVersion is required") } if pkg.RunID == "" { return fmt.Errorf("runId is required") } if pkg.Report.ID == "" { return fmt.Errorf("report.id is required") } if pkg.Report.PromptID == "" { return fmt.Errorf("report.promptId is required") } if pkg.Report.GeneratedAt.IsZero() { return fmt.Errorf("report.generatedAt is required") } if pkg.Report.Timezone == "" { return fmt.Errorf("report.timezone is required") } if pkg.Report.CurrentLocalDate == "" { return fmt.Errorf("report.currentLocalDate is required") } if !pkg.Report.ValidPeriod.IsValid() { return fmt.Errorf("report.validPeriod must be valid") } if pkg.Briefing.Metadata.RunID == "" { return fmt.Errorf("briefing.metadata.runId is required") } if pkg.Briefing.Metadata.RunID != pkg.RunID { return fmt.Errorf("briefing.metadata.runId must match runId") } if pkg.Briefing.Metadata.SchemaVersion == "" { return fmt.Errorf("briefing.metadata.schemaVersion is required") } if pkg.Briefing.Metadata.PromptID != pkg.Report.PromptID { return fmt.Errorf("briefing.metadata.promptId must match report.promptId") } if pkg.Briefing.Metadata.ReportID != pkg.Report.ID { return fmt.Errorf("briefing.metadata.reportId must match report.id") } if pkg.Briefing.Daily == nil && pkg.Briefing.ThreeDay == nil && pkg.Briefing.Weekend == nil && pkg.Briefing.Storm == nil { return fmt.Errorf("briefing report content is required") } return nil } func Save(path string, pkg Package) error { if err := Validate(pkg); err != nil { return err } if err := fileutil.WriteJSONAtomic(path, pkg); err != nil { return fmt.Errorf("save data package: %w", err) } return nil }