diff --git a/db/helpers.go b/db/helpers.go index 7c2237d..05edd65 100644 --- a/db/helpers.go +++ b/db/helpers.go @@ -507,6 +507,7 @@ WITH daily AS ( ), totals AS ( SELECT COALESCE(SUM(total_samples_day), 0) AS total_samples FROM enriched ) +-- monthly averages are weighted by the implied sample counts per day (SamplesPresent / AvgIsPresent) INSERT INTO %s ( "InventoryId", "Name", "Vcenter", "VmId", "EventKey", "CloudId", "CreationTime", "DeletionTime", "ResourcePool", "Datacenter", "Cluster", "Folder", "ProvisionedDisk", "VcpuCount", diff --git a/internal/report/snapshots.go b/internal/report/snapshots.go index ff23ad4..c0a7b21 100644 --- a/internal/report/snapshots.go +++ b/internal/report/snapshots.go @@ -760,7 +760,7 @@ func addTotalsChartSheet(logger *slog.Logger, database db.Database, ctx context. if err != nil || len(records) == 0 { return } - points, err := buildDailyTotals(ctx, database.DB(), records) + points, err := buildDailyTotals(ctx, database.DB(), records, true) if err != nil || len(points) == 0 { return } @@ -1042,11 +1042,12 @@ WHERE %s return nil, err } points = append(points, totalsPoint{ - Label: record.SnapshotTime.Local().Format("2006-01-02 15:04"), - VmCount: row.VmCount, - VcpuTotal: float64(row.VcpuTotal), - RamTotal: float64(row.RamTotal), - PresenceRatio: row.PresenceRatio, + Label: record.SnapshotTime.Local().Format("2006-01-02 15:04"), + VmCount: row.VmCount, + VcpuTotal: float64(row.VcpuTotal), + RamTotal: float64(row.RamTotal), + // For hourly snapshots, prorated VM count equals VM count (no finer granularity). + PresenceRatio: float64(row.VmCount), TinTotal: row.TinTotal, BronzeTotal: row.BronzeTotal, SilverTotal: row.SilverTotal, @@ -1056,7 +1057,7 @@ WHERE %s return points, nil } -func buildDailyTotals(ctx context.Context, dbConn *sqlx.DB, records []SnapshotRecord) ([]totalsPoint, error) { +func buildDailyTotals(ctx context.Context, dbConn *sqlx.DB, records []SnapshotRecord, prorateByAvg bool) ([]totalsPoint, error) { points := make([]totalsPoint, 0, len(records)) for _, record := range records { if err := db.ValidateTableName(record.TableName); err != nil { @@ -1096,7 +1097,7 @@ WHERE %s VmCount: row.VmCount, VcpuTotal: row.VcpuTotal, RamTotal: row.RamTotal, - PresenceRatio: row.PresenceRatio, + PresenceRatio: computeProratedVmCount(row.PresenceRatio, row.VmCount, prorateByAvg), TinTotal: row.TinTotal, BronzeTotal: row.BronzeTotal, SilverTotal: row.SilverTotal, @@ -1106,6 +1107,13 @@ WHERE %s return points, nil } +func computeProratedVmCount(presenceRatio float64, vmCount int64, prorate bool) float64 { + if !prorate { + return float64(vmCount) + } + return presenceRatio * float64(vmCount) +} + func writeTotalsChart(logger *slog.Logger, xlsx *excelize.File, sheetName string, points []totalsPoint) { if len(points) == 0 { return @@ -1162,11 +1170,12 @@ func writeTotalsChart(logger *slog.Logger, xlsx *excelize.File, sheetName string series = append(series, buildSeries(col)) } chart := excelize.Chart{ - Type: excelize.Line, - Series: series, - Legend: excelize.ChartLegend{Position: "bottom"}, - XAxis: excelize.ChartAxis{MajorGridLines: true}, - YAxis: excelize.ChartAxis{MajorGridLines: true}, + Type: excelize.Line, + Series: series, + Legend: excelize.ChartLegend{Position: "bottom"}, + XAxis: excelize.ChartAxis{MajorGridLines: true}, + YAxis: excelize.ChartAxis{MajorGridLines: true}, + Dimension: excelize.ChartDimension{Width: 960, Height: 480}, } if err := xlsx.AddChart(sheetName, anchor, &chart); err != nil { logger.Error("Error adding totals chart", "error", err, "anchor", anchor) @@ -1174,8 +1183,8 @@ func writeTotalsChart(logger *slog.Logger, xlsx *excelize.File, sheetName string } makeChart("K2", "B", "E") - makeChart("K18", "C", "D") - makeChart("K34", "F", "G", "H", "I") + makeChart("K27", "C", "D") + makeChart("K52", "F", "G", "H", "I") } func formatEpochHuman(value interface{}) string {