~bigbes/lethe

ref: 4abe63ea5d809ec3a7e87e349ccc9493a377b1bc lethe/internal/domain/stats/buckets.go -rw-r--r-- 2.2 KiB
4abe63ea — Eugene Blikh docs: conclude lethe collector task 24 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// Package stats implements the read-only /api/v1/stats aggregation API. It
// bundles six queries into one round-trip and fills deterministic time windows
// (daily, heatmap, hour-of-day) so missing slots appear as zeros on the wire.
package stats

import "time"

// DailyWindow returns days+1 unix seconds (at UTC midnight), oldest first,
// ending today. Example: days=7 returns 8 entries — today plus the 7 prior
// days. Each pair is exactly 86400 seconds apart.
//
// The boundary rule: time.Unix(now,0).UTC().Truncate(24h) gives today's
// midnight in UTC.
func DailyWindow(now int64, days int) []int64 {
	today := time.Unix(now, 0).UTC().Truncate(24 * time.Hour).Unix()
	out := make([]int64, days+1)
	for i := 0; i <= days; i++ {
		// oldest first: index 0 is `days` days before today
		out[i] = today - int64(days-i)*86400
	}
	return out
}

// FillDaily left-joins rows onto the window. Each window slot appears exactly
// once in the output (oldest first). Missing slots get PerTool: map[string]int64{}
// (empty map, not nil — so JSON encodes as {}). Rows whose DateUnix does not
// match any window slot are silently dropped (they are outside the range).
func FillDaily(window []int64, rows []DailyBucket) []DailyBucket {
	// Build a lookup map from the rows.
	rowByDate := make(map[int64]DailyBucket, len(rows))
	for _, r := range rows {
		rowByDate[r.DateUnix] = r
	}

	out := make([]DailyBucket, len(window))
	for i, ts := range window {
		if row, ok := rowByDate[ts]; ok {
			out[i] = row
		} else {
			out[i] = DailyBucket{DateUnix: ts, PerTool: map[string]int64{}}
		}
	}
	return out
}

// HeatmapWindow returns 84 unix seconds (at UTC midnight), oldest first,
// ending today — 12 weeks × 7 days. The length is fixed regardless of the
// request's range.
func HeatmapWindow(now int64) []int64 {
	const cells = 84 // 12 weeks × 7 days
	today := time.Unix(now, 0).UTC().Truncate(24 * time.Hour).Unix()
	out := make([]int64, cells)
	for i := 0; i < cells; i++ {
		out[i] = today - int64(cells-1-i)*86400
	}
	return out
}

// HourWindow returns 24 zero-initialised HourBucket values, one per hour
// (Hour 0..23), in ascending order.
func HourWindow() []HourBucket {
	out := make([]HourBucket, 24)
	for i := range out {
		out[i] = HourBucket{Hour: i, Count: 0}
	}
	return out
}