// Package report defines report identities, registry metadata, and valid periods. package report import ( "fmt" "strings" "time" "gitea.maximumdirect.net/eric/weatherreporter/internal/module" "gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil" ) type ID string const ( Daily ID = "daily" Today ID = "today" Tomorrow ID = "tomorrow" Hourly ID = "hourly" ) type Batch string const ( Morning Batch = "morning" Evening Batch = "evening" ) type Definition struct { ID ID Name string PromptID string PromptVersion string TemplateID string GeneratedTextSchemaID string ArtifactGroup string OutputName string DistributorPathTemplates []string Modules []module.ConfigItem Morning bool Evening bool resolve func(ResolveRequest) (timeutil.Period, error) runIDDisambiguator func(Resolved) string } func (r Resolved) OutputName() (string, error) { if r.Definition.ID == Daily { if r.ValidPeriod.Start.IsZero() { return "", fmt.Errorf("daily report has no valid-period start for output naming") } location, err := timeutil.LoadLocation(r.Timezone) if err != nil { return "", fmt.Errorf("load report timezone for output naming: %w", err) } return "daily-" + r.ValidPeriod.Start.In(location).Format(timeutil.DateLayout) + ".md", nil } if r.Definition.OutputName == "" { return "", fmt.Errorf("report %q has no output name", r.Definition.ID) } return r.Definition.OutputName, nil } func (d Definition) ResolvePeriod(req ResolveRequest) (timeutil.Period, error) { if d.resolve == nil { return timeutil.Period{}, fmt.Errorf("report %q has no valid-period resolver", d.ID) } return d.resolve(req) } func (d Definition) ModuleIDs() []module.ID { ids := make([]module.ID, 0, len(d.Modules)) for _, item := range d.Modules { ids = append(ids, item.ID) } return ids } type ResolveRequest struct { Now time.Time Location *time.Location Date time.Time } type Resolved struct { Definition Definition GeneratedAt time.Time Timezone string ValidPeriod timeutil.Period } type Metadata struct { RunID string `json:"runId"` ReportID ID `json:"reportId"` PromptID string `json:"promptId"` GeneratedAt time.Time `json:"generatedAt"` Timezone string `json:"timezone"` ValidPeriod timeutil.Period `json:"validPeriod"` } func (r Resolved) Metadata() Metadata { return Metadata{ RunID: r.runID(), ReportID: r.Definition.ID, PromptID: r.Definition.PromptID, GeneratedAt: r.GeneratedAt, Timezone: r.Timezone, ValidPeriod: r.ValidPeriod, } } func (r Resolved) runID() string { runID := r.GeneratedAt.UTC().Format("20060102T150405.000000000Z") + "_" + string(r.Definition.ID) if r.Definition.runIDDisambiguator == nil { return runID } disambiguator := strings.TrimSpace(r.Definition.runIDDisambiguator(r)) if disambiguator == "" { return runID } return runID + "_" + disambiguator }