2026-06-09 21:33:34 +00:00
|
|
|
// Package briefing builds prompt-facing module values.
|
2026-05-29 17:23:49 +00:00
|
|
|
package briefing
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
|
|
|
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
|
2026-06-09 20:16:47 +00:00
|
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
|
2026-05-29 17:23:49 +00:00
|
|
|
)
|
|
|
|
|
|
2026-08-13 02:00:18 +00:00
|
|
|
// PreparedIdentity is the single prepared authority for report identity,
|
|
|
|
|
// timing, configuration context, and source warnings.
|
|
|
|
|
type PreparedIdentity struct {
|
2026-06-09 20:16:47 +00:00
|
|
|
RunID string `json:"runId"`
|
|
|
|
|
ReportID report.ID `json:"reportId"`
|
|
|
|
|
Variant string `json:"variant,omitempty"`
|
|
|
|
|
PromptID string `json:"promptId"`
|
|
|
|
|
GeneratedAt time.Time `json:"generatedAt"`
|
|
|
|
|
Units string `json:"units"`
|
|
|
|
|
Timezone string `json:"timezone"`
|
|
|
|
|
ValidPeriod timeutil.Period `json:"validPeriod"`
|
|
|
|
|
Location *LocationContext `json:"location,omitempty"`
|
|
|
|
|
SourceLocationID string `json:"sourceLocationId,omitempty"`
|
|
|
|
|
SourceLocation string `json:"sourceLocation,omitempty"`
|
|
|
|
|
SourceWarnings []weatherdata.SourceWarning `json:"sourceWarnings,omitempty"`
|
2026-08-13 01:52:11 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-29 19:57:08 -05:00
|
|
|
type LocationContext struct {
|
|
|
|
|
ID string `json:"id,omitempty"`
|
|
|
|
|
Name string `json:"name,omitempty"`
|
|
|
|
|
Region string `json:"region,omitempty"`
|
|
|
|
|
Timezone string `json:"timezone,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-29 17:23:49 +00:00
|
|
|
type BuildContext struct {
|
|
|
|
|
Resolved report.Resolved
|
2026-06-09 20:16:47 +00:00
|
|
|
Bundle *weatherdata.Bundle
|
2026-05-29 17:23:49 +00:00
|
|
|
Units string
|
|
|
|
|
Timezone string
|
2026-05-29 19:57:08 -05:00
|
|
|
Location *LocationContext
|
2026-05-29 17:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
2026-08-13 01:52:11 +00:00
|
|
|
func BuildPreparedIdentity(ctx BuildContext) PreparedIdentity {
|
2026-05-29 17:23:49 +00:00
|
|
|
metadata := ctx.Resolved.Metadata()
|
2026-08-13 02:00:18 +00:00
|
|
|
sourceLocationID, sourceLocation := sourceLocation(ctx.Bundle)
|
2026-08-13 01:52:11 +00:00
|
|
|
return PreparedIdentity{
|
2026-08-13 02:00:18 +00:00
|
|
|
RunID: metadata.RunID,
|
|
|
|
|
ReportID: metadata.ReportID,
|
|
|
|
|
Variant: variantForReport(metadata.ReportID),
|
|
|
|
|
PromptID: metadata.PromptID,
|
|
|
|
|
GeneratedAt: metadata.GeneratedAt,
|
|
|
|
|
Units: ctx.Units,
|
|
|
|
|
Timezone: ctx.Timezone,
|
|
|
|
|
ValidPeriod: metadata.ValidPeriod,
|
|
|
|
|
Location: copyLocation(ctx.Location),
|
2026-05-29 17:23:49 +00:00
|
|
|
SourceLocationID: sourceLocationID,
|
|
|
|
|
SourceLocation: sourceLocation,
|
2026-08-13 02:00:18 +00:00
|
|
|
SourceWarnings: append([]weatherdata.SourceWarning(nil), sourceWarnings(ctx.Bundle)...),
|
2026-05-29 17:23:49 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-29 19:57:08 -05:00
|
|
|
func copyLocation(location *LocationContext) *LocationContext {
|
|
|
|
|
if location == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
copied := *location
|
|
|
|
|
return &copied
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-29 20:04:27 -05:00
|
|
|
func copyBool(value *bool) *bool {
|
|
|
|
|
if value == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
copied := *value
|
|
|
|
|
return &copied
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 10:00:36 -05:00
|
|
|
func copyInt(value *int) *int {
|
|
|
|
|
if value == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
copied := *value
|
|
|
|
|
return &copied
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-29 20:04:27 -05:00
|
|
|
func copyFloat(value *float64) *float64 {
|
|
|
|
|
if value == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
copied := *value
|
|
|
|
|
return &copied
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-09 21:33:34 +00:00
|
|
|
func copyTime(value *time.Time) *time.Time {
|
|
|
|
|
if value == nil {
|
|
|
|
|
return nil
|
2026-05-29 17:23:49 +00:00
|
|
|
}
|
2026-06-09 21:33:34 +00:00
|
|
|
copied := *value
|
|
|
|
|
return &copied
|
2026-05-29 17:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
2026-06-09 20:16:47 +00:00
|
|
|
func sourceLocation(bundle *weatherdata.Bundle) (string, string) {
|
2026-05-29 17:23:49 +00:00
|
|
|
if bundle == nil {
|
|
|
|
|
return "", ""
|
|
|
|
|
}
|
2026-06-09 20:16:47 +00:00
|
|
|
for _, run := range []*weatherdata.ForecastRun{bundle.Hourly, bundle.Narrative, bundle.Daily} {
|
2026-05-29 17:23:49 +00:00
|
|
|
if run == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if run.LocationID != "" || run.LocationName != "" {
|
|
|
|
|
return run.LocationID, run.LocationName
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "", ""
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-09 20:16:47 +00:00
|
|
|
func sourceWarnings(bundle *weatherdata.Bundle) []weatherdata.SourceWarning {
|
2026-05-29 17:23:49 +00:00
|
|
|
if bundle == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return bundle.Warnings
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func variantForReport(id report.ID) string {
|
|
|
|
|
switch id {
|
2026-06-15 16:43:06 +00:00
|
|
|
case report.Daily, report.Today:
|
2026-05-29 17:23:49 +00:00
|
|
|
return "today"
|
2026-06-14 23:22:31 +00:00
|
|
|
case report.Tomorrow:
|
2026-05-29 17:23:49 +00:00
|
|
|
return "tomorrow"
|
|
|
|
|
default:
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
}
|