ggdgsdbsdbbb / admin / admin_stats.v
203 lines · 187 sloc · 5.01 KB · f7e2568a041118007cc5b746666a79a4ee9974c5
Raw
1module main
2
3import time
4import veb
5
6const admin_stats_days = 30
7const stats_month_short = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct',
8 'Nov', 'Dec']
9
10struct DayBucket {
11 label string
12 count int
13}
14
15struct AdminStats {
16mut:
17 days int
18 users []DayBucket
19 repos []DayBucket
20 commits []DayBucket
21 issues []DayBucket
22 total_users int
23 total_repos int
24 total_commits int
25 total_issues int
26 max_users int
27 max_repos int
28 max_commits int
29 max_issues int
30}
31
32fn stats_day_label(ts i64) string {
33 t := time.unix(ts)
34 return '${stats_month_short[t.month - 1]} ${t.day}'
35}
36
37fn stats_bucket_index(ts i64, range_start i64, one_day i64, days int) int {
38 if ts < range_start {
39 return -1
40 }
41 idx := int((ts - range_start) / one_day)
42 if idx < 0 || idx >= days {
43 return -1
44 }
45 return idx
46}
47
48pub fn (mut app App) get_admin_stats(days int) AdminStats {
49 one_day := i64(86400)
50 today_start := time.now().unix() / one_day * one_day
51 range_start := today_start - i64(days - 1) * one_day
52
53 mut user_counts := []int{len: days, init: 0}
54 mut repo_counts := []int{len: days, init: 0}
55 mut commit_counts := []int{len: days, init: 0}
56 mut issue_counts := []int{len: days, init: 0}
57
58 registered_users := sql app.db {
59 select from User where is_registered == true
60 } or { []User{} }
61 for u in registered_users {
62 idx := stats_bucket_index(u.created_at.unix(), range_start, one_day, days)
63 if idx >= 0 {
64 user_counts[idx]++
65 }
66 }
67
68 repo_rows := db_exec_values(app.db,
69 'select created_at from ${sql_table('Repo')} where created_at >= ${range_start}') or {
70 [][]string{}
71 }
72 for row in repo_rows {
73 if row.len == 0 {
74 continue
75 }
76 idx := stats_bucket_index(row[0].i64(), range_start, one_day, days)
77 if idx >= 0 {
78 repo_counts[idx]++
79 }
80 }
81
82 commit_rows := db_exec_values(app.db,
83 'select created_at from ${sql_table('Commit')} where created_at >= ${range_start}') or {
84 [][]string{}
85 }
86 for row in commit_rows {
87 if row.len == 0 {
88 continue
89 }
90 idx := stats_bucket_index(row[0].i64(), range_start, one_day, days)
91 if idx >= 0 {
92 commit_counts[idx]++
93 }
94 }
95
96 issue_rows := db_exec_values(app.db,
97 'select created_at from ${sql_table('Issue')} where is_pr is false and created_at >= ${range_start}') or {
98 [][]string{}
99 }
100 for row in issue_rows {
101 if row.len == 0 {
102 continue
103 }
104 idx := stats_bucket_index(row[0].i64(), range_start, one_day, days)
105 if idx >= 0 {
106 issue_counts[idx]++
107 }
108 }
109
110 mut user_series := []DayBucket{cap: days}
111 mut repo_series := []DayBucket{cap: days}
112 mut commit_series := []DayBucket{cap: days}
113 mut issue_series := []DayBucket{cap: days}
114 mut max_u := 0
115 mut max_r := 0
116 mut max_c := 0
117 mut max_i := 0
118 for i in 0 .. days {
119 ts := range_start + i64(i) * one_day
120 lbl := stats_day_label(ts)
121 user_series << DayBucket{lbl, user_counts[i]}
122 repo_series << DayBucket{lbl, repo_counts[i]}
123 commit_series << DayBucket{lbl, commit_counts[i]}
124 issue_series << DayBucket{lbl, issue_counts[i]}
125 if user_counts[i] > max_u {
126 max_u = user_counts[i]
127 }
128 if repo_counts[i] > max_r {
129 max_r = repo_counts[i]
130 }
131 if commit_counts[i] > max_c {
132 max_c = commit_counts[i]
133 }
134 if issue_counts[i] > max_i {
135 max_i = issue_counts[i]
136 }
137 }
138
139 total_users := sql app.db {
140 select count from User where is_registered == true
141 } or { 0 }
142 total_repos := sql app.db {
143 select count from Repo
144 } or { 0 }
145 total_commits := sql app.db {
146 select count from Commit
147 } or { 0 }
148 total_issues := sql app.db {
149 select count from Issue where is_pr == false
150 } or { 0 }
151
152 return AdminStats{
153 days: days
154 users: user_series
155 repos: repo_series
156 commits: commit_series
157 issues: issue_series
158 total_users: total_users
159 total_repos: total_repos
160 total_commits: total_commits
161 total_issues: total_issues
162 max_users: max_u
163 max_repos: max_r
164 max_commits: max_c
165 max_issues: max_i
166 }
167}
168
169fn render_stat_chart(buckets []DayBucket, max int, color string) veb.RawHtml {
170 chart_w := 720
171 chart_h := 200
172 bar_area_h := 160
173 bar_top := 10
174 bar_count := buckets.len
175 if bar_count == 0 {
176 return veb.RawHtml('')
177 }
178 slot := (chart_w - 20) / bar_count
179 bar_w := if slot > 4 { slot - 2 } else { slot }
180 mut s := '<svg class="stat-chart" viewBox="0 0 ${chart_w} ${chart_h}" preserveAspectRatio="none">'
181 s += '<g class="stat-chart-grid">'
182 for i in 1 .. 5 {
183 y := bar_top + bar_area_h - bar_area_h * i / 4
184 s += '<line x1="10" y1="${y}" x2="${chart_w - 10}" y2="${y}"></line>'
185 }
186 s += '</g>'
187 for i, b in buckets {
188 h := if max == 0 { 0 } else { b.count * bar_area_h / max }
189 x := 10 + i * slot
190 y := bar_top + bar_area_h - h
191 s += '<rect class="stat-chart-bar" x="${x}" y="${y}" width="${bar_w}" height="${h}" fill="${color}">'
192 s += '<title>${b.label}: ${b.count}</title></rect>'
193 }
194 label_y := chart_h - 6
195 for i, b in buckets {
196 if i % 5 == 0 || i == buckets.len - 1 {
197 x := 10 + i * slot + bar_w / 2
198 s += '<text class="stat-chart-label" x="${x}" y="${label_y}" text-anchor="middle">${b.label}</text>'
199 }
200 }
201 s += '</svg>'
202 return veb.RawHtml(s)
203}
204