Generate Daily reports through scriptorium

This commit is contained in:
2026-05-29 17:47:42 +00:00
parent 7ac73f758b
commit b17a3591e0
14 changed files with 543 additions and 113 deletions

View File

@@ -4,6 +4,8 @@ package app
import (
"context"
"fmt"
"os"
"path/filepath"
"time"
"gitea.maximumdirect.net/eric/weatherreporter/internal/adapters/scriptorium"
@@ -59,12 +61,12 @@ type DailyBriefingRequest struct {
OutputPath string
}
type DailyPreparationRequest struct {
Config config.Config
Resolved report.Resolved
DataPackagePath string
Renderer Renderer
Store state.Store
type DailyReportRequest struct {
Config config.Config
Resolved report.Resolved
OutputPath string
Renderer Renderer
Store state.Store
}
type DailyBriefingResult struct {
@@ -72,20 +74,24 @@ type DailyBriefingResult struct {
OutputPath string
}
type DailyPreparationResult struct {
type DailyReportResult struct {
Briefing briefing.Package
BriefingPath string
DataPackage promptinput.Package
DataPackagePath string
PreflightPath string
ReportPath string
OutputPath string
Metadata state.Metadata
MetadataPath string
PriorSnapshot *state.PriorSnapshot
RenderResult *scriptorium.RenderResult
RunResult *scriptorium.RunResult
}
type Renderer interface {
Render(context.Context, scriptorium.RenderRequest) (*scriptorium.RenderResult, error)
Run(context.Context, scriptorium.RunRequest) (*scriptorium.RunResult, error)
}
func Generate(ctx context.Context, req GenerateRequest) error {
@@ -94,10 +100,10 @@ func Generate(ctx context.Context, req GenerateRequest) error {
return err
}
if resolved.Definition.ID == report.DailyToday {
_, err := PrepareDailyReport(ctx, DailyPreparationRequest{
Config: req.Config,
Resolved: resolved,
DataPackagePath: req.OutputPath,
_, err := GenerateDailyReport(ctx, DailyReportRequest{
Config: req.Config,
Resolved: resolved,
OutputPath: req.OutputPath,
})
return err
}
@@ -226,7 +232,7 @@ func GenerateDailyBriefing(ctx context.Context, req DailyBriefingRequest) (*Dail
return &DailyBriefingResult{Package: pkg, OutputPath: outputPath}, nil
}
func PrepareDailyReport(ctx context.Context, req DailyPreparationRequest) (*DailyPreparationResult, error) {
func GenerateDailyReport(ctx context.Context, req DailyReportRequest) (*DailyReportResult, error) {
store := req.Store
if store == nil {
defaultStore, err := defaultStore(req.Config)
@@ -269,11 +275,6 @@ func PrepareDailyReport(ctx context.Context, req DailyPreparationRequest) (*Dail
if err != nil {
return nil, err
}
if req.DataPackagePath != "" && req.DataPackagePath != dataPackagePath {
if err := promptinput.Save(req.DataPackagePath, dataPackage); err != nil {
return nil, err
}
}
renderer := req.Renderer
if renderer == nil {
@@ -313,16 +314,46 @@ func PrepareDailyReport(ctx context.Context, req DailyPreparationRequest) (*Dail
return nil, renderErr
}
return &DailyPreparationResult{
reportPath, err := store.PrepareRenderedReport(ctx, req.Resolved)
if err != nil {
return nil, err
}
runResult, runErr := renderer.Run(ctx, scriptorium.RunRequest{
PromptID: req.Resolved.Definition.PromptID,
DataPackagePath: dataPackagePath,
OutputPath: reportPath,
})
if runErr == nil && req.OutputPath != "" && req.OutputPath != reportPath {
if err := copyFileAtomic(reportPath, req.OutputPath); err != nil {
return nil, err
}
}
outputPath := reportPath
if req.OutputPath != "" {
outputPath = req.OutputPath
}
metadata.RenderedReportPath = reportPath
metadataPath, metadataErr = store.SaveMetadata(ctx, metadata)
if metadataErr != nil {
return nil, metadataErr
}
if runErr != nil {
return nil, runErr
}
return &DailyReportResult{
Briefing: briefingPackage,
BriefingPath: briefingPath,
DataPackage: dataPackage,
DataPackagePath: dataPackagePath,
PreflightPath: preflightPath,
ReportPath: reportPath,
OutputPath: outputPath,
Metadata: metadata,
MetadataPath: metadataPath,
PriorSnapshot: priorSnapshot,
RenderResult: renderResult,
RunResult: runResult,
}, nil
}
@@ -354,3 +385,30 @@ func BuildDailyBriefing(req DailyBriefingRequest, bundle *forecast.Bundle) (brie
func defaultStore(cfg config.Config) (*state.FilesystemStore, error) {
return state.NewFilesystemStore(cfg.Workspace)
}
func copyFileAtomic(source string, target string) error {
data, err := os.ReadFile(source)
if err != nil {
return fmt.Errorf("read rendered report %q: %w", source, err)
}
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
return fmt.Errorf("create report output directory %q: %w", filepath.Dir(target), err)
}
tmp, err := os.CreateTemp(filepath.Dir(target), "."+filepath.Base(target)+".*.tmp")
if err != nil {
return fmt.Errorf("create temporary report output file: %w", err)
}
tmpName := tmp.Name()
defer os.Remove(tmpName)
if _, err := tmp.Write(data); err != nil {
tmp.Close()
return fmt.Errorf("write temporary report output file: %w", err)
}
if err := tmp.Close(); err != nil {
return fmt.Errorf("close temporary report output file: %w", err)
}
if err := os.Rename(tmpName, target); err != nil {
return fmt.Errorf("save report output %q: %w", target, err)
}
return nil
}