Files
weatherreporter/internal/briefing/package.go

196 lines
5.2 KiB
Go

// Package briefing builds prompt-facing module values.
package briefing
import (
"time"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
)
type Metadata struct {
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"`
Sources []SourceMetadata `json:"sources,omitempty"`
SourceWarnings []weatherdata.SourceWarning `json:"sourceWarnings,omitempty"`
Alerts *AlertStatus `json:"alerts,omitempty"`
}
type LocationContext struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Region string `json:"region,omitempty"`
Timezone string `json:"timezone,omitempty"`
}
type SourceMetadata struct {
Name string `json:"name"`
Endpoint string `json:"endpoint,omitempty"`
FetchedAt time.Time `json:"fetchedAt"`
IssuedAt *time.Time `json:"issuedAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
DataSHA256 string `json:"dataSha256,omitempty"`
Missing bool `json:"missing,omitempty"`
Warnings []weatherdata.SourceWarning `json:"warnings,omitempty"`
}
type AlertStatus struct {
Checked bool `json:"checked"`
ActiveCount int `json:"activeCount"`
RelevantCount int `json:"relevantCount"`
Missing bool `json:"missing,omitempty"`
}
type BuildContext struct {
Resolved report.Resolved
Bundle *weatherdata.Bundle
Units string
Timezone string
Location *LocationContext
}
func BuildMetadata(ctx BuildContext) Metadata {
metadata := ctx.Resolved.Metadata()
sourceLocationID, sourceLocation := sourceLocation(ctx.Bundle)
return Metadata{
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),
SourceLocationID: sourceLocationID,
SourceLocation: sourceLocation,
Sources: sourceMetadata(ctx.Bundle),
SourceWarnings: sourceWarnings(ctx.Bundle),
Alerts: alertStatus(ctx.Bundle),
}
}
func copyLocation(location *LocationContext) *LocationContext {
if location == nil {
return nil
}
copied := *location
return &copied
}
func copyBool(value *bool) *bool {
if value == nil {
return nil
}
copied := *value
return &copied
}
func copyInt(value *int) *int {
if value == nil {
return nil
}
copied := *value
return &copied
}
func copyFloat(value *float64) *float64 {
if value == nil {
return nil
}
copied := *value
return &copied
}
func copyTime(value *time.Time) *time.Time {
if value == nil {
return nil
}
copied := *value
return &copied
}
func sourceLocation(bundle *weatherdata.Bundle) (string, string) {
if bundle == nil {
return "", ""
}
for _, run := range []*weatherdata.ForecastRun{bundle.Hourly, bundle.Narrative, bundle.Daily} {
if run == nil {
continue
}
if run.LocationID != "" || run.LocationName != "" {
return run.LocationID, run.LocationName
}
}
return "", ""
}
func sourceMetadata(bundle *weatherdata.Bundle) []SourceMetadata {
if bundle == nil {
return nil
}
out := make([]SourceMetadata, 0, len(bundle.Sources))
for _, source := range bundle.Sources {
out = append(out, SourceMetadata{
Name: source.Name,
Endpoint: source.Endpoint,
FetchedAt: source.FetchedAt,
IssuedAt: source.IssuedAt,
UpdatedAt: source.UpdatedAt,
DataSHA256: source.DataSHA256,
Missing: source.Missing,
Warnings: source.Warnings,
})
}
return out
}
func sourceWarnings(bundle *weatherdata.Bundle) []weatherdata.SourceWarning {
if bundle == nil {
return nil
}
return bundle.Warnings
}
func alertStatus(bundle *weatherdata.Bundle) *AlertStatus {
if bundle == nil {
return nil
}
status := &AlertStatus{}
if bundle.Alerts != nil {
status.Checked = true
status.ActiveCount = len(bundle.Alerts.Alerts)
}
for _, source := range bundle.Sources {
if source.Name == "alerts" && source.Missing {
status.Missing = true
break
}
}
if !status.Checked && !status.Missing {
return nil
}
return status
}
func variantForReport(id report.ID) string {
switch id {
case report.Daily, report.Today:
return "today"
case report.Tomorrow:
return "tomorrow"
default:
return ""
}
}