Wire hourly generated text rendering
This commit is contained in:
@@ -17,9 +17,11 @@ import (
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/facts"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/fileutil"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/forecast"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/generatedtext"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/promptinput"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/reporttemplate"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/state"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
|
||||
@@ -89,21 +91,26 @@ type ReportRequest struct {
|
||||
}
|
||||
|
||||
type ReportResult struct {
|
||||
ModuleSnapshot module.Snapshot
|
||||
ModuleSnapshotPath string
|
||||
DataPackage promptinput.Package
|
||||
DataPackagePath string
|
||||
PreflightPath string
|
||||
ReportPath string
|
||||
OutputPath string
|
||||
NotificationPath string
|
||||
Metadata state.Metadata
|
||||
MetadataPath string
|
||||
PriorSnapshot *state.PriorSnapshot
|
||||
RecentChanges []changes.Change
|
||||
RenderResult *scriptorium.RenderResult
|
||||
RunResult *scriptorium.RunResult
|
||||
Notification *NotificationResult
|
||||
ModuleSnapshot module.Snapshot
|
||||
ModuleSnapshotPath string
|
||||
DataPackage promptinput.Package
|
||||
DataPackagePath string
|
||||
PreflightPath string
|
||||
ReportPath string
|
||||
OutputPath string
|
||||
NotificationPath string
|
||||
Metadata state.Metadata
|
||||
MetadataPath string
|
||||
PriorSnapshot *state.PriorSnapshot
|
||||
RecentChanges []changes.Change
|
||||
RenderResult *scriptorium.RenderResult
|
||||
RunResult *scriptorium.RunResult
|
||||
StructuredRunResult *scriptorium.StructuredRunResult
|
||||
GeneratedTextRawPath string
|
||||
GeneratedTextResultPath string
|
||||
GeneratedTextPath string
|
||||
RenderContextPath string
|
||||
Notification *NotificationResult
|
||||
}
|
||||
|
||||
type BatchResult struct {
|
||||
@@ -151,6 +158,7 @@ func (e BatchError) Error() string {
|
||||
type Renderer interface {
|
||||
Render(context.Context, scriptorium.RenderRequest) (*scriptorium.RenderResult, error)
|
||||
Run(context.Context, scriptorium.RunRequest) (*scriptorium.RunResult, error)
|
||||
StructuredRun(context.Context, scriptorium.StructuredRunRequest) (*scriptorium.StructuredRunResult, error)
|
||||
}
|
||||
|
||||
type Notifier interface {
|
||||
@@ -438,9 +446,6 @@ func FetchAndSaveBundle(ctx context.Context, req FetchBundleRequest) (*weatherda
|
||||
}
|
||||
|
||||
func GenerateReport(ctx context.Context, req ReportRequest) (*ReportResult, error) {
|
||||
if req.Resolved.Definition.GenerationMode != report.GenerationModeScriptoriumMarkdown {
|
||||
return nil, fmt.Errorf("generation mode %q is not implemented for report %q", req.Resolved.Definition.GenerationMode, req.Resolved.Definition.ID)
|
||||
}
|
||||
store := req.Store
|
||||
if store == nil {
|
||||
defaultStore, err := defaultStore(req.Config)
|
||||
@@ -488,11 +493,15 @@ func GenerateReport(ctx context.Context, req ReportRequest) (*ReportResult, erro
|
||||
}
|
||||
briefingMetadata := briefing.BuildMetadata(briefingBuildContext(req.Config, req.Resolved, reportFacts.Collected))
|
||||
metadata := state.BuildMetadataFromBriefingMetadata(req.Resolved, briefingMetadata, state.ArtifactPaths{
|
||||
ModuleSnapshot: moduleSnapshotPath,
|
||||
Metadata: paths.Metadata,
|
||||
DataPackage: paths.DataPackage,
|
||||
Preflight: paths.Preflight,
|
||||
RenderedReport: paths.RenderedReport,
|
||||
ModuleSnapshot: moduleSnapshotPath,
|
||||
Metadata: paths.Metadata,
|
||||
DataPackage: paths.DataPackage,
|
||||
Preflight: paths.Preflight,
|
||||
RenderedReport: paths.RenderedReport,
|
||||
GeneratedTextRaw: paths.GeneratedTextRaw,
|
||||
GeneratedTextResult: paths.GeneratedTextResult,
|
||||
GeneratedText: paths.GeneratedText,
|
||||
RenderContext: paths.RenderContext,
|
||||
})
|
||||
dataPackage, err := promptinput.Build(promptinput.BuildRequest{
|
||||
Metadata: promptMetadata(metadata),
|
||||
@@ -540,6 +549,28 @@ func GenerateReport(ctx context.Context, req ReportRequest) (*ReportResult, erro
|
||||
return nil, renderErr
|
||||
}
|
||||
|
||||
if req.Resolved.Definition.GenerationMode == report.GenerationModeGeneratedTextTemplate {
|
||||
return generateTextTemplateReport(ctx, generatedReportRequest{
|
||||
ReportRequest: req,
|
||||
store: store,
|
||||
paths: paths,
|
||||
moduleSnapshot: moduleSnapshot,
|
||||
moduleSnapshotPath: moduleSnapshotPath,
|
||||
dataPackage: dataPackage,
|
||||
dataPackagePath: dataPackagePath,
|
||||
briefingMetadata: briefingMetadata,
|
||||
metadata: metadata,
|
||||
metadataPath: metadataPath,
|
||||
preflightPath: preflightPath,
|
||||
recentChanges: recentChanges,
|
||||
renderResult: renderResult,
|
||||
renderer: renderer,
|
||||
})
|
||||
}
|
||||
if req.Resolved.Definition.GenerationMode != report.GenerationModeScriptoriumMarkdown {
|
||||
return nil, fmt.Errorf("generation mode %q is not supported for report %q", req.Resolved.Definition.GenerationMode, req.Resolved.Definition.ID)
|
||||
}
|
||||
|
||||
reportPath, err := store.PrepareRenderedReport(ctx, req.Resolved)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -598,6 +629,107 @@ func GenerateReport(ctx context.Context, req ReportRequest) (*ReportResult, erro
|
||||
}, nil
|
||||
}
|
||||
|
||||
type generatedReportRequest struct {
|
||||
ReportRequest
|
||||
store state.Store
|
||||
paths state.ArtifactPaths
|
||||
moduleSnapshot module.Snapshot
|
||||
moduleSnapshotPath string
|
||||
dataPackage promptinput.Package
|
||||
dataPackagePath string
|
||||
briefingMetadata briefing.Metadata
|
||||
metadata state.Metadata
|
||||
metadataPath string
|
||||
preflightPath string
|
||||
recentChanges []changes.Change
|
||||
renderResult *scriptorium.RenderResult
|
||||
renderer Renderer
|
||||
}
|
||||
|
||||
func generateTextTemplateReport(ctx context.Context, req generatedReportRequest) (*ReportResult, error) {
|
||||
structuredResult, runErr := req.renderer.StructuredRun(ctx, scriptorium.StructuredRunRequest{
|
||||
PromptID: req.Resolved.Definition.PromptID,
|
||||
DataPackagePath: req.dataPackagePath,
|
||||
OutputPath: req.paths.GeneratedTextRaw,
|
||||
})
|
||||
generatedTextResultPath := req.paths.GeneratedTextResult
|
||||
if structuredResult != nil {
|
||||
var err error
|
||||
generatedTextResultPath, err = req.store.SaveGeneratedTextResult(ctx, req.Resolved, structuredResult)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.metadata.GeneratedTextResultPath = generatedTextResultPath
|
||||
req.metadataPath, err = req.store.SaveMetadata(ctx, req.metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if runErr != nil {
|
||||
return nil, runErr
|
||||
}
|
||||
|
||||
rawGeneratedText, err := req.store.LoadGeneratedText(ctx, req.paths.GeneratedTextRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hourlyText, normalizedGeneratedText, err := validateGeneratedText(req.Resolved.Definition, rawGeneratedText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
generatedTextPath, err := req.store.SaveGeneratedText(ctx, req.Resolved, normalizedGeneratedText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.metadata.GeneratedTextPath = generatedTextPath
|
||||
|
||||
renderContext, err := buildRenderContext(req.Resolved.Definition, req.briefingMetadata, req.moduleSnapshot, hourlyText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
renderContextPath, err := req.store.SaveRenderContext(ctx, req.Resolved, renderContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.metadata.RenderContextPath = renderContextPath
|
||||
|
||||
rendered, err := reporttemplate.Render(req.Resolved.Definition.TemplateID, renderContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reportPath, err := req.store.PrepareRenderedReport(ctx, req.Resolved)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := fileutil.WriteFileAtomic(reportPath, rendered); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.metadata.RenderedReportPath = reportPath
|
||||
metadataPath, err := req.store.SaveMetadata(ctx, req.metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ReportResult{
|
||||
ModuleSnapshot: req.moduleSnapshot,
|
||||
ModuleSnapshotPath: req.moduleSnapshotPath,
|
||||
DataPackage: req.dataPackage,
|
||||
DataPackagePath: req.dataPackagePath,
|
||||
PreflightPath: req.preflightPath,
|
||||
ReportPath: reportPath,
|
||||
OutputPath: reportPath,
|
||||
Metadata: req.metadata,
|
||||
MetadataPath: metadataPath,
|
||||
RecentChanges: req.recentChanges,
|
||||
RenderResult: req.renderResult,
|
||||
StructuredRunResult: structuredResult,
|
||||
GeneratedTextRawPath: req.paths.GeneratedTextRaw,
|
||||
GeneratedTextResultPath: generatedTextResultPath,
|
||||
GeneratedTextPath: generatedTextPath,
|
||||
RenderContextPath: renderContextPath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func notifyReport(ctx context.Context, cfg config.Config, resolved report.Resolved, reportPath string, metadata state.Metadata, notifier Notifier, store state.Store) (*NotificationResult, string, error) {
|
||||
notifier, enabled := reportNotifier(cfg, notifier)
|
||||
if !enabled {
|
||||
@@ -941,3 +1073,21 @@ func preflightArtifact(result *scriptorium.RenderResult) state.PreflightArtifact
|
||||
ExitCode: result.ExitCode,
|
||||
}
|
||||
}
|
||||
|
||||
func validateGeneratedText(definition report.Definition, data []byte) (generatedtext.Hourly, []byte, error) {
|
||||
switch definition.GeneratedTextSchemaID {
|
||||
case "hourly":
|
||||
return generatedtext.ValidateHourly(data)
|
||||
default:
|
||||
return generatedtext.Hourly{}, nil, fmt.Errorf("generated text schema %q is not supported for report %q", definition.GeneratedTextSchemaID, definition.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func buildRenderContext(definition report.Definition, metadata briefing.Metadata, snapshot module.Snapshot, hourly generatedtext.Hourly) (any, error) {
|
||||
switch definition.TemplateID {
|
||||
case "hourly":
|
||||
return generatedtext.BuildHourlyRenderContext(metadata, snapshot, hourly)
|
||||
default:
|
||||
return nil, fmt.Errorf("report template %q is not supported for report %q", definition.TemplateID, definition.ID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,9 +349,15 @@ func TestGenerateReportOmitsSPCConvectiveDiscussionBelowThreshold(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateHourlyReportModeNotImplemented(t *testing.T) {
|
||||
func TestGenerateHourlyReportUsesGeneratedTextTemplateWorkflow(t *testing.T) {
|
||||
server := hourlyBundleServer(t)
|
||||
cfg := config.Defaults()
|
||||
cfg.WeatherAPI.BaseURL = server.URL + "/"
|
||||
cfg.WeatherAPI.Timezone = "America/Chicago"
|
||||
cfg.Workspace.Root = t.TempDir()
|
||||
cfg.Location.ID = "home"
|
||||
cfg.Location.Name = "Brentwood"
|
||||
cfg.Location.Region = "MO"
|
||||
resolved, err := ResolveGenerate(GenerateRequest{
|
||||
Config: cfg,
|
||||
Report: ReportHourly,
|
||||
@@ -362,14 +368,136 @@ func TestGenerateHourlyReportModeNotImplemented(t *testing.T) {
|
||||
if resolved.Definition.GenerationMode != report.GenerationModeGeneratedTextTemplate {
|
||||
t.Fatalf("GenerationMode = %q, want generated text template", resolved.Definition.GenerationMode)
|
||||
}
|
||||
filesystemStore, err := state.NewFilesystemStore(cfg.Workspace)
|
||||
if err != nil {
|
||||
t.Fatalf("NewFilesystemStore() error = %v", err)
|
||||
}
|
||||
store := &recordingStore{Store: filesystemStore}
|
||||
renderer := &recordingRenderer{
|
||||
renderResult: &scriptorium.RenderResult{
|
||||
Command: []string{"scriptorium", "render"},
|
||||
Stdout: `{"prepared":true}`,
|
||||
ExitCode: 0,
|
||||
},
|
||||
structuredRunResult: &scriptorium.StructuredRunResult{
|
||||
Command: []string{"scriptorium", "run"},
|
||||
Stderr: "wrote generated text",
|
||||
ExitCode: 0,
|
||||
},
|
||||
structuredRunBody: `{
|
||||
"summary": " Storm chances increase through late morning. ",
|
||||
"timing": "The main window is 10 AM to noon.",
|
||||
"impacts": "Brief downpours may slow travel.",
|
||||
"confidence": "Medium"
|
||||
}`,
|
||||
}
|
||||
|
||||
_, err = GenerateReport(context.Background(), ReportRequest{
|
||||
result, err := GenerateReport(context.Background(), ReportRequest{
|
||||
Config: cfg,
|
||||
Resolved: resolved,
|
||||
Renderer: successfulRenderer("# Hourly Report\n"),
|
||||
Renderer: renderer,
|
||||
Store: store,
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), `generation mode "generated_text_template" is not implemented for report "hourly"`) {
|
||||
t.Fatalf("GenerateReport() error = %v, want generation mode not implemented", err)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateReport() error = %v", err)
|
||||
}
|
||||
|
||||
if renderer.renderCalls != 1 {
|
||||
t.Fatalf("render calls = %d, want 1", renderer.renderCalls)
|
||||
}
|
||||
if renderer.structuredRunCalls != 1 {
|
||||
t.Fatalf("structured run calls = %d, want 1", renderer.structuredRunCalls)
|
||||
}
|
||||
if renderer.runCalls != 0 {
|
||||
t.Fatalf("markdown run calls = %d, want none", renderer.runCalls)
|
||||
}
|
||||
if renderer.structuredRunRequest.OutputPath != result.GeneratedTextRawPath {
|
||||
t.Fatalf("structured run OutputPath = %q, want %q", renderer.structuredRunRequest.OutputPath, result.GeneratedTextRawPath)
|
||||
}
|
||||
if renderer.structuredRunRequest.DataPackagePath != result.DataPackagePath {
|
||||
t.Fatalf("structured run DataPackagePath = %q, want %q", renderer.structuredRunRequest.DataPackagePath, result.DataPackagePath)
|
||||
}
|
||||
if got, want := strings.Join(store.calls, ","), "module_snapshot,data_package,preflight,metadata,generated_text_result,metadata,generated_text,render_context,prepare_report,metadata"; got != want {
|
||||
t.Fatalf("store calls = %v, want %s", store.calls, want)
|
||||
}
|
||||
|
||||
assertPathsExist(t,
|
||||
result.ModuleSnapshotPath,
|
||||
result.DataPackagePath,
|
||||
result.PreflightPath,
|
||||
result.GeneratedTextRawPath,
|
||||
result.GeneratedTextResultPath,
|
||||
result.GeneratedTextPath,
|
||||
result.RenderContextPath,
|
||||
result.ReportPath,
|
||||
result.MetadataPath,
|
||||
)
|
||||
raw, err := os.ReadFile(result.GeneratedTextRawPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read raw generated text: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(raw), `"summary": " Storm chances increase through late morning. "`) {
|
||||
t.Fatalf("raw generated text was not preserved:\n%s", string(raw))
|
||||
}
|
||||
normalized, err := os.ReadFile(result.GeneratedTextPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read validated generated text: %v", err)
|
||||
}
|
||||
if string(normalized) != `{"summary":"Storm chances increase through late morning.","timing":"The main window is 10 AM to noon.","impacts":"Brief downpours may slow travel.","confidence":"Medium"}` {
|
||||
t.Fatalf("validated generated text = %s, want normalized JSON", normalized)
|
||||
}
|
||||
renderContext, err := os.ReadFile(result.RenderContextPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read render context: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(renderContext), `"ReportTitle": "Hourly Report"`) || !strings.Contains(string(renderContext), `"LocationName": "Brentwood, MO"`) {
|
||||
t.Fatalf("render context missing deterministic fields:\n%s", string(renderContext))
|
||||
}
|
||||
reportData, err := os.ReadFile(result.ReportPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read report: %v", err)
|
||||
}
|
||||
reportText := string(reportData)
|
||||
for _, want := range []string{
|
||||
"# Hourly Report",
|
||||
"Storm chances increase through late morning.",
|
||||
"The main window is 10 AM to noon.",
|
||||
"Flood Watch: Flooding possible (Moderate)",
|
||||
"Short-term AFD narrative for hourly report.",
|
||||
"Hourly Storm Chances - Scattered showers and thunderstorms are possible.",
|
||||
} {
|
||||
if !strings.Contains(reportText, want) {
|
||||
t.Fatalf("rendered hourly report missing %q:\n%s", want, reportText)
|
||||
}
|
||||
}
|
||||
if result.OutputPath != result.ReportPath {
|
||||
t.Fatalf("OutputPath = %q, want managed report path %q", result.OutputPath, result.ReportPath)
|
||||
}
|
||||
if result.StructuredRunResult == nil || result.StructuredRunResult.OutputPath != result.GeneratedTextRawPath {
|
||||
t.Fatalf("StructuredRunResult = %#v, want captured structured run result", result.StructuredRunResult)
|
||||
}
|
||||
if result.RunResult != nil {
|
||||
t.Fatalf("RunResult = %#v, want nil for generated-text template workflow", result.RunResult)
|
||||
}
|
||||
if len(result.RecentChanges) != 0 {
|
||||
t.Fatalf("RecentChanges = %#v, want none for hourly report", result.RecentChanges)
|
||||
}
|
||||
if result.Metadata.GeneratedTextSchemaID != "hourly" ||
|
||||
result.Metadata.GeneratedTextRawPath != result.GeneratedTextRawPath ||
|
||||
result.Metadata.GeneratedTextResultPath != result.GeneratedTextResultPath ||
|
||||
result.Metadata.GeneratedTextPath != result.GeneratedTextPath ||
|
||||
result.Metadata.RenderContextPath != result.RenderContextPath ||
|
||||
result.Metadata.RenderedReportPath != result.ReportPath {
|
||||
t.Fatalf("metadata generated-text links = %#v, want saved artifact links", result.Metadata)
|
||||
}
|
||||
metadataData, err := os.ReadFile(result.MetadataPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read metadata: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(metadataData), `"generatedTextSchemaId": "hourly"`) ||
|
||||
!strings.Contains(string(metadataData), result.GeneratedTextRawPath) ||
|
||||
!strings.Contains(string(metadataData), result.RenderContextPath) {
|
||||
t.Fatalf("metadata JSON missing generated-text links:\n%s", string(metadataData))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1715,15 +1843,20 @@ func appBriefingMetadata(resolved report.Resolved) briefing.Metadata {
|
||||
}
|
||||
|
||||
type recordingRenderer struct {
|
||||
renderCalls int
|
||||
runCalls int
|
||||
renderRequest scriptorium.RenderRequest
|
||||
runRequest scriptorium.RunRequest
|
||||
renderResult *scriptorium.RenderResult
|
||||
runResult *scriptorium.RunResult
|
||||
err error
|
||||
runErr error
|
||||
runBody string
|
||||
renderCalls int
|
||||
runCalls int
|
||||
structuredRunCalls int
|
||||
renderRequest scriptorium.RenderRequest
|
||||
runRequest scriptorium.RunRequest
|
||||
structuredRunRequest scriptorium.StructuredRunRequest
|
||||
renderResult *scriptorium.RenderResult
|
||||
runResult *scriptorium.RunResult
|
||||
structuredRunResult *scriptorium.StructuredRunResult
|
||||
err error
|
||||
runErr error
|
||||
structuredRunErr error
|
||||
runBody string
|
||||
structuredRunBody string
|
||||
}
|
||||
|
||||
type recordingStore struct {
|
||||
@@ -1746,6 +1879,26 @@ func (s *recordingStore) SavePreflight(ctx context.Context, resolved report.Reso
|
||||
return s.Store.SavePreflight(ctx, resolved, artifact)
|
||||
}
|
||||
|
||||
func (s *recordingStore) SaveGeneratedTextRaw(ctx context.Context, resolved report.Resolved, data []byte) (string, error) {
|
||||
s.calls = append(s.calls, "generated_text_raw")
|
||||
return s.Store.SaveGeneratedTextRaw(ctx, resolved, data)
|
||||
}
|
||||
|
||||
func (s *recordingStore) SaveGeneratedTextResult(ctx context.Context, resolved report.Resolved, value any) (string, error) {
|
||||
s.calls = append(s.calls, "generated_text_result")
|
||||
return s.Store.SaveGeneratedTextResult(ctx, resolved, value)
|
||||
}
|
||||
|
||||
func (s *recordingStore) SaveGeneratedText(ctx context.Context, resolved report.Resolved, data []byte) (string, error) {
|
||||
s.calls = append(s.calls, "generated_text")
|
||||
return s.Store.SaveGeneratedText(ctx, resolved, data)
|
||||
}
|
||||
|
||||
func (s *recordingStore) SaveRenderContext(ctx context.Context, resolved report.Resolved, value any) (string, error) {
|
||||
s.calls = append(s.calls, "render_context")
|
||||
return s.Store.SaveRenderContext(ctx, resolved, value)
|
||||
}
|
||||
|
||||
func (s *recordingStore) PrepareRenderedReport(ctx context.Context, resolved report.Resolved) (string, error) {
|
||||
s.calls = append(s.calls, "prepare_report")
|
||||
return s.Store.PrepareRenderedReport(ctx, resolved)
|
||||
@@ -1765,10 +1918,11 @@ func successfulRenderer(body string) *recordingRenderer {
|
||||
}
|
||||
|
||||
type selectiveRenderer struct {
|
||||
renderCalls int
|
||||
runCalls int
|
||||
failRenderPrompt string
|
||||
runBody string
|
||||
renderCalls int
|
||||
runCalls int
|
||||
structuredRunCalls int
|
||||
failRenderPrompt string
|
||||
runBody string
|
||||
}
|
||||
|
||||
type recordingNotifier struct {
|
||||
@@ -1826,6 +1980,11 @@ func (r *selectiveRenderer) Run(_ context.Context, req scriptorium.RunRequest) (
|
||||
return &scriptorium.RunResult{ExitCode: 0, OutputPath: req.OutputPath}, nil
|
||||
}
|
||||
|
||||
func (r *selectiveRenderer) StructuredRun(_ context.Context, req scriptorium.StructuredRunRequest) (*scriptorium.StructuredRunResult, error) {
|
||||
r.structuredRunCalls++
|
||||
return &scriptorium.StructuredRunResult{ExitCode: 0, OutputPath: req.OutputPath}, nil
|
||||
}
|
||||
|
||||
func (r *recordingRenderer) Render(_ context.Context, req scriptorium.RenderRequest) (*scriptorium.RenderResult, error) {
|
||||
r.renderCalls++
|
||||
r.renderRequest = req
|
||||
@@ -1845,3 +2004,17 @@ func (r *recordingRenderer) Run(_ context.Context, req scriptorium.RunRequest) (
|
||||
}
|
||||
return r.runResult, r.runErr
|
||||
}
|
||||
|
||||
func (r *recordingRenderer) StructuredRun(_ context.Context, req scriptorium.StructuredRunRequest) (*scriptorium.StructuredRunResult, error) {
|
||||
r.structuredRunCalls++
|
||||
r.structuredRunRequest = req
|
||||
if r.structuredRunBody != "" {
|
||||
if err := os.WriteFile(req.OutputPath, []byte(r.structuredRunBody), 0o600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if r.structuredRunResult != nil {
|
||||
r.structuredRunResult.OutputPath = req.OutputPath
|
||||
}
|
||||
return r.structuredRunResult, r.structuredRunErr
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ Options:
|
||||
--config PATH Load configuration from PATH instead of /usr/local/etc/weatherreporter/config.yml.
|
||||
--units VALUE Override weather API units.
|
||||
--tz NAME Override weather API timezone.
|
||||
--out PATH Write an extra Markdown report copy for generate commands.
|
||||
--out PATH Write an extra Markdown report copy where supported by the generate command.
|
||||
--out-dir PATH Write extra Markdown report copies for run commands.
|
||||
`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user