internal/httpd/activity.go
64 lines · 1651 bytes
1package httpd
2
3import "time"
4
5// activityDay is one cell of the graph; Level buckets Count into the five
6// intensity classes the stylesheet colors.
7type activityDay struct {
8 Date string
9 Count int
10 Level int // 0..4
11 Pad bool // before the range start / after today
12}
13
14type activityWeek []activityDay // 7 days, Sunday first
15
16// activityGrid lays a day->count map into 53 week columns ending today,
17// GitHub-style: columns are weeks, rows Sunday..Saturday.
18func activityGrid(counts map[string]int) ([]activityWeek, int) {
19 today := time.Now().UTC()
20 // End the grid on the Saturday of the current week.
21 end := today.AddDate(0, 0, int(time.Saturday-today.Weekday()))
22 start := end.AddDate(0, 0, -53*7+1) // a Sunday, 53 columns back
23
24 total := 0
25 var weeks []activityWeek
26 for d := start; !d.After(end); d = d.AddDate(0, 0, 7) {
27 var week activityWeek
28 for i := 0; i < 7; i++ {
29 day := d.AddDate(0, 0, i)
30 key := day.Format("2006-01-02")
31 if day.After(today) {
32 week = append(week, activityDay{Date: key, Pad: true})
33 continue
34 }
35 n := counts[key]
36 total += n
37 week = append(week, activityDay{Date: key, Count: n, Level: activityLevel(n)})
38 }
39 weeks = append(weeks, week)
40 }
41 return weeks, total
42}
43
44func activityLevel(n int) int {
45 switch {
46 case n == 0:
47 return 0
48 case n <= 2:
49 return 1
50 case n <= 5:
51 return 2
52 case n <= 9:
53 return 3
54 default:
55 return 4
56 }
57}
58
59// activitySince is the first day the grid can show, for the query bound.
60func activitySince() string {
61 today := time.Now().UTC()
62 end := today.AddDate(0, 0, int(time.Saturday-today.Weekday()))
63 return end.AddDate(0, 0, -53*7+1).Format("2006-01-02")
64}