internal/httpd/activity.go
79 lines · 2043 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
14// activityWeek is one column of the graph: 7 days, Sunday first, plus the
15// month label to show above it (empty for most weeks).
16type activityWeek struct {
17 Days []activityDay
18 Month string
19}
20
21// activityGrid lays a day->count map into 53 week columns ending today,
22// GitHub-style: columns are weeks, rows Sunday..Saturday. Each week whose
23// first non-pad day falls within the first 7 days of a month, and whose
24// month differs from the last labelled week, carries that month's
25// three-letter name.
26func activityGrid(counts map[string]int) ([]activityWeek, int) {
27 today := time.Now().UTC()
28 // End the grid on the Saturday of the current week.
29 end := today.AddDate(0, 0, int(time.Saturday-today.Weekday()))
30 start := end.AddDate(0, 0, -53*7+1) // a Sunday, 53 columns back
31
32 total := 0
33 var weeks []activityWeek
34 prevMonth := ""
35 for d := start; !d.After(end); d = d.AddDate(0, 0, 7) {
36 var days []activityDay
37 var firstDay time.Time
38 haveFirst := false
39 for i := 0; i < 7; i++ {
40 day := d.AddDate(0, 0, i)
41 key := day.Format("2006-01-02")
42 if day.After(today) {
43 days = append(days, activityDay{Date: key, Pad: true})
44 continue
45 }
46 if !haveFirst {
47 firstDay = day
48 haveFirst = true
49 }
50 n := counts[key]
51 total += n
52 days = append(days, activityDay{Date: key, Count: n, Level: activityLevel(n)})
53 }
54 month := ""
55 if haveFirst && firstDay.Day() <= 7 {
56 if name := firstDay.Month().String()[:3]; name != prevMonth {
57 month = name
58 prevMonth = name
59 }
60 }
61 weeks = append(weeks, activityWeek{Days: days, Month: month})
62 }
63 return weeks, total
64}
65
66func activityLevel(n int) int {
67 switch {
68 case n == 0:
69 return 0
70 case n <= 2:
71 return 1
72 case n <= 5:
73 return 2
74 case n <= 9:
75 return 3
76 default:
77 return 4
78 }
79}