From 8c540b6bdc745ee0c0eab7cb6075d5289ec45873 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 27 Jun 2026 11:17:03 +0300 Subject: Restyle HTML output to stats.foo.zone style; bump to 0.5.3 --- internal/daemon/daemon_test.go | 2 +- internal/goprecords/report.go | 132 +++++++++++++++++++++++++++++------ internal/goprecords/report_format.go | 71 +++++++++++-------- internal/goprecords/report_test.go | 4 +- internal/version/version.go | 2 +- 5 files changed, 154 insertions(+), 57 deletions(-) (limited to 'internal') diff --git a/internal/daemon/daemon_test.go b/internal/daemon/daemon_test.go index 44f9888..5d642b6 100644 --- a/internal/daemon/daemon_test.go +++ b/internal/daemon/daemon_test.go @@ -312,7 +312,7 @@ func TestReportHTTPTable(t *testing.T) { query: "OutputFormat=HTML&limit=2", wantCode: http.StatusOK, wantCTPfx: "text/html", - bodyNeedle: []string{"", "
"},
+			bodyNeedle: []string{"", ""},
 		},
 		{
 			name:      "gemtext",
diff --git a/internal/goprecords/report.go b/internal/goprecords/report.go
index 12c3951..8916caa 100644
--- a/internal/goprecords/report.go
+++ b/internal/goprecords/report.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"io"
 	"sort"
+	"strings"
 )
 
 type metricExtractor struct {
@@ -61,38 +62,33 @@ func WriteReports(w io.Writer, aggregates *Aggregates, cfg ReportConfig) error {
 		if cfg.Category != CategoryHost && (cfg.Metric == MetricDowntime || cfg.Metric == MetricLifespan) {
 			return fmt.Errorf("Category %s only supports: Boots, Uptime, Score", cfg.Category)
 		}
-		if cfg.Category == CategoryHost {
-			if err := writeReportString(w, NewHostReporter(aggregates, cfg.Limit, cfg.Metric, cfg.OutputFormat, 1).Report()); err != nil {
-				return err
-			}
-		} else {
-			if err := writeReportString(w, NewReporter(aggregates, cfg.Category, cfg.Limit, cfg.Metric, cfg.OutputFormat, 1).Report()); err != nil {
-				return err
-			}
-		}
-		return nil
+		s := reportForPair(aggregates, cfg.Category, cfg.Metric, cfg.Limit, 1, cfg.OutputFormat)
+		return writeReportString(w, wrapIfHTML(s, cfg.OutputFormat))
 	}
 	order, err := StatsOrderList(cfg.StatsOrder)
 	if err != nil {
 		return err
 	}
 	headerIndent := uint(2)
-	for _, pair := range order {
-		c, m := pair.Category, pair.Metric
-		if !cfg.IncludeKernel && c == CategoryKernel {
-			continue
+	if cfg.OutputFormat == FormatHTML {
+		var parts []string
+		for _, pair := range order {
+			if skipPair(cfg, pair.Category, pair.Metric) {
+				continue
+			}
+			if s := reportForPair(aggregates, pair.Category, pair.Metric, cfg.Limit, headerIndent, cfg.OutputFormat); s != "" {
+				parts = append(parts, s)
+			}
 		}
-		if c != CategoryHost && (m == MetricDowntime || m == MetricLifespan) {
+		return writeReportString(w, wrapHTMLDocument(strings.Join(parts, "\n")))
+	}
+	for _, pair := range order {
+		if skipPair(cfg, pair.Category, pair.Metric) {
 			continue
 		}
-		if c == CategoryHost {
-			if err := writeReportString(w, NewHostReporter(aggregates, cfg.Limit, m, cfg.OutputFormat, headerIndent).Report()); err != nil {
-				return err
-			}
-		} else {
-			if err := writeReportString(w, NewReporter(aggregates, c, cfg.Limit, m, cfg.OutputFormat, headerIndent).Report()); err != nil {
-				return err
-			}
+		s := reportForPair(aggregates, pair.Category, pair.Metric, cfg.Limit, headerIndent, cfg.OutputFormat)
+		if err := writeReportString(w, s); err != nil {
+			return err
 		}
 		if err := writeReportString(w, "\n"); err != nil {
 			return err
@@ -101,6 +97,96 @@ func WriteReports(w io.Writer, aggregates *Aggregates, cfg ReportConfig) error {
 	return nil
 }
 
+func reportForPair(aggregates *Aggregates, c Category, m Metric, limit, headerIndent uint, outputFormat OutputFormat) string {
+	if c == CategoryHost {
+		return NewHostReporter(aggregates, limit, m, outputFormat, headerIndent).Report()
+	}
+	return NewReporter(aggregates, c, limit, m, outputFormat, headerIndent).Report()
+}
+
+func skipPair(cfg ReportConfig, c Category, m Metric) bool {
+	if !cfg.IncludeKernel && c == CategoryKernel {
+		return true
+	}
+	if c != CategoryHost && (m == MetricDowntime || m == MetricLifespan) {
+		return true
+	}
+	return false
+}
+
+func wrapIfHTML(s string, f OutputFormat) string {
+	if f != FormatHTML {
+		return s
+	}
+	return wrapHTMLDocument(s)
+}
+
+func wrapHTMLDocument(body string) string {
+	var b strings.Builder
+	b.WriteString("\n\n\n")
+	b.WriteString("\n")
+	b.WriteString("\n")
+	b.WriteString("goprecords uptime report\n")
+	b.WriteString(htmlStyle)
+	b.WriteString("\n\n")
+	b.WriteString(body)
+	b.WriteString("\n\n")
+	return b.String()
+}
+
+const htmlStyle = `
+`
+
 func writeReportString(w io.Writer, s string) error {
 	_, err := io.WriteString(w, s)
 	return err
diff --git a/internal/goprecords/report_format.go b/internal/goprecords/report_format.go
index 8d06b04..079fe94 100644
--- a/internal/goprecords/report_format.go
+++ b/internal/goprecords/report_format.go
@@ -21,22 +21,6 @@ func (r reportBuilder) formatReport(rows []tableRow, hasLastKernel, hasLastUpdat
 }
 
 func (r reportBuilder) formatReportHTML(rows []tableRow, hasLastKernel, hasLastUpdated bool) string {
-	cW, nW, vW, lkW, luW := r.reportWidths(rows, hasLastKernel, hasLastUpdated)
-	border := r.buildBorder(cW, nW, vW, lkW, luW, hasLastKernel, hasLastUpdated)
-	fmtStr := r.buildFormatStr(cW, nW, vW, lkW, luW, hasLastKernel, hasLastUpdated)
-	var headRow string
-	if hasLastKernel && hasLastUpdated {
-		headRow = fmt.Sprintf(fmtStr+"\n", "Pos", r.category.String(), r.metric.String(), "Last Kernel", "Updated")
-	} else if hasLastKernel {
-		headRow = fmt.Sprintf(fmtStr+"\n", "Pos", r.category.String(), r.metric.String(), "Last Kernel")
-	} else if hasLastUpdated {
-		headRow = fmt.Sprintf(fmtStr+"\n", "Pos", r.category.String(), r.metric.String(), "Updated")
-	} else {
-		headRow = fmt.Sprintf(fmtStr+"\n", "Pos", r.category.String(), r.metric.String())
-	}
-	body := r.buildReportBody(rows, fmtStr, hasLastKernel, hasLastUpdated)
-	ascii := border + headRow + border + body + border
-
 	hl := int(r.headerIndent)
 	if hl < 1 {
 		hl = 1
@@ -46,27 +30,54 @@ func (r reportBuilder) formatReportHTML(rows []tableRow, hasLastKernel, hasLastU
 	}
 	title := fmt.Sprintf("Top %d %s's by %s", r.limit, r.metric, r.category)
 	desc := MetricDescription(r.metric)
+	htag := strconv.Itoa(hl)
 
 	var b strings.Builder
-	b.WriteString("\n\n\n\n")
-	b.WriteString("\n")
-	b.WriteString("\n")
-	b.WriteString(template.HTMLEscapeString(title))
-	b.WriteString("\n\n\n")
+	b.WriteString("")
 	b.WriteString(template.HTMLEscapeString(title))
 	b.WriteString("
\n
\n") + b.WriteString(htag) + b.WriteString(">\n") if desc != "" { - b.WriteString("") + b.WriteString("

") b.WriteString(template.HTMLEscapeString(desc)) - b.WriteString("
\n
\n") + b.WriteString("

\n") + } + b.WriteString("
\n") + if hasLastKernel { + b.WriteString("") + } + if hasLastUpdated { + b.WriteString("") + } + b.WriteString("\n") + for _, row := range rows { + b.WriteString("") + if hasLastKernel { + b.WriteString("") + } + if hasLastUpdated { + b.WriteString("") + } + b.WriteString("\n") } - b.WriteString("
")
-	b.WriteString(template.HTMLEscapeString(ascii))
-	b.WriteString("
\n\n\n") + b.WriteString("
Pos") + b.WriteString(template.HTMLEscapeString(r.category.String())) + b.WriteString("") + b.WriteString(template.HTMLEscapeString(r.metric.String())) + b.WriteString("Last KernelUpdated
") + b.WriteString(template.HTMLEscapeString(row.Pos)) + b.WriteString("") + b.WriteString(template.HTMLEscapeString(row.Name)) + b.WriteString("") + b.WriteString(template.HTMLEscapeString(row.Value)) + b.WriteString("") + b.WriteString(template.HTMLEscapeString(row.LastKernel)) + b.WriteString("") + b.WriteString(template.HTMLEscapeString(row.LastUpdated)) + b.WriteString("
\n") return b.String() } diff --git a/internal/goprecords/report_test.go b/internal/goprecords/report_test.go index fccb0d2..962afdb 100644 --- a/internal/goprecords/report_test.go +++ b/internal/goprecords/report_test.go @@ -114,8 +114,8 @@ func TestReportHTML(t *testing.T) { t.Fatalf("expected HTMLReporter, got %T", reporter) } report := reporter.Report() - if !strings.Contains(report, "") || !strings.Contains(report, "
") {
-		t.Fatalf("expected HTML document with pre, got %q", report)
+	if !strings.Contains(report, "") || !strings.Contains(report, "

") { + t.Fatalf("expected HTML fragment with table, got %q", report) } if !strings.Contains(report, "host1") { t.Error("expected report to contain host1") diff --git a/internal/version/version.go b/internal/version/version.go index 7ee9a7c..1b13709 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -1,4 +1,4 @@ package version // Tag is the application release version. -const Tag = "0.5.2" +const Tag = "0.5.3" -- cgit v1.2.3