146 lines
4.6 KiB
Go
146 lines
4.6 KiB
Go
// Package promptinput builds prompt data packages from briefing packages.
|
|
package promptinput
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/briefing"
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/changes"
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
|
|
)
|
|
|
|
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 []forecast.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"`
|
|
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) {
|
|
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,
|
|
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 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.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
|
|
}
|
|
data, err := json.MarshalIndent(pkg, "", " ")
|
|
if err != nil {
|
|
return fmt.Errorf("marshal data package: %w", err)
|
|
}
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
return fmt.Errorf("create data package directory %q: %w", filepath.Dir(path), err)
|
|
}
|
|
tmp, err := os.CreateTemp(filepath.Dir(path), "."+filepath.Base(path)+".*.tmp")
|
|
if err != nil {
|
|
return fmt.Errorf("create temporary data package file: %w", err)
|
|
}
|
|
tmpName := tmp.Name()
|
|
defer os.Remove(tmpName)
|
|
|
|
if _, err := tmp.Write(data); err != nil {
|
|
tmp.Close()
|
|
return fmt.Errorf("write temporary data package file: %w", err)
|
|
}
|
|
if err := tmp.Close(); err != nil {
|
|
return fmt.Errorf("close temporary data package file: %w", err)
|
|
}
|
|
if err := os.Rename(tmpName, path); err != nil {
|
|
return fmt.Errorf("save data package %q: %w", path, err)
|
|
}
|
|
return nil
|
|
}
|