summaryrefslogtreecommitdiffabout
path: root/ui-stats.c
authorLars Hjemli <hjemli@gmail.com>2008-12-07 12:17:21 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-12-07 12:17:21 (UTC)
commitfb2f3f6c29bad733723152893c5246a756e4cada (patch) (unidiff)
tree5b2953c0c116f276ca48beee676a6662b6329d95 /ui-stats.c
parentc6a6aa2186daf39814baa0e71378c2e9e1041002 (diff)
downloadcgit-fb2f3f6c29bad733723152893c5246a756e4cada.zip
cgit-fb2f3f6c29bad733723152893c5246a756e4cada.tar.gz
cgit-fb2f3f6c29bad733723152893c5246a756e4cada.tar.bz2
ui-stats: replace 'enable-stats' setting with 'max-stats'
The new 'max-stats' and 'repo.max-stats' settings makes it possible to define the maximum statistics period, both globally and per repo. Hence, it is now feasible to allow statistics on repositories with a high commit frequency, like linux-2.6, by setting repo.max-stats to e.g. 'month'. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (limited to 'ui-stats.c') (more/less context) (ignore whitespace changes)
-rw-r--r--ui-stats.c97
1 files changed, 58 insertions, 39 deletions
diff --git a/ui-stats.c b/ui-stats.c
index 3cc8d70..1104485 100644
--- a/ui-stats.c
+++ b/ui-stats.c
@@ -1,29 +1,15 @@
1#include <string-list.h>
2
1#include "cgit.h" 3#include "cgit.h"
2#include "html.h" 4#include "html.h"
3#include <string-list.h> 5#include "ui-shared.h"
6#include "ui-stats.h"
4 7
5#define MONTHS 6 8#define MONTHS 6
6 9
7struct Period {
8 const char code;
9 const char *name;
10 int max_periods;
11 int count;
12
13 /* Convert a tm value to the first day in the period */
14 void (*trunc)(struct tm *tm);
15
16 /* Update tm value to start of next/previous period */
17 void (*dec)(struct tm *tm);
18 void (*inc)(struct tm *tm);
19
20 /* Pretty-print a tm value */
21 char *(*pretty)(struct tm *tm);
22};
23
24struct authorstat { 10struct authorstat {
25 long total; 11 long total;
26 struct string_list list; 12 struct string_list list;
27}; 13};
28 14
29#define DAY_SECS (60 * 60 * 24) 15#define DAY_SECS (60 * 60 * 24)
@@ -134,21 +120,45 @@ static void inc_year(struct tm *tm)
134 120
135static char *pretty_year(struct tm *tm) 121static char *pretty_year(struct tm *tm)
136{ 122{
137 return fmt("%d", tm->tm_year + 1900); 123 return fmt("%d", tm->tm_year + 1900);
138} 124}
139 125
140struct Period periods[] = { 126struct cgit_period periods[] = {
141 {'w', "week", 12, 4, trunc_week, dec_week, inc_week, pretty_week}, 127 {'w', "week", 12, 4, trunc_week, dec_week, inc_week, pretty_week},
142 {'m', "month", 12, 4, trunc_month, dec_month, inc_month, pretty_month}, 128 {'m', "month", 12, 4, trunc_month, dec_month, inc_month, pretty_month},
143 {'q', "quarter", 12, 4, trunc_quarter, dec_quarter, inc_quarter, pretty_quarter}, 129 {'q', "quarter", 12, 4, trunc_quarter, dec_quarter, inc_quarter, pretty_quarter},
144 {'y', "year", 12, 4, trunc_year, dec_year, inc_year, pretty_year}, 130 {'y', "year", 12, 4, trunc_year, dec_year, inc_year, pretty_year},
145}; 131};
146 132
133/* Given a period code or name, return a period index (1, 2, 3 or 4)
134 * and update the period pointer to the correcsponding struct.
135 * If no matching code is found, return 0.
136 */
137int cgit_find_stats_period(const char *expr, struct cgit_period **period)
138{
139 int i;
140 char code = '\0';
141
142 if (!expr)
143 return 0;
144
145 if (strlen(expr) == 1)
146 code = expr[0];
147
148 for (i = 0; i < sizeof(periods) / sizeof(periods[0]); i++)
149 if (periods[i].code == code || !strcmp(periods[i].name, expr)) {
150 if (period)
151 *period = &periods[i];
152 return i+1;
153 }
154 return 0;
155}
156
147static void add_commit(struct string_list *authors, struct commit *commit, 157static void add_commit(struct string_list *authors, struct commit *commit,
148 struct Period *period) 158 struct cgit_period *period)
149{ 159{
150 struct commitinfo *info; 160 struct commitinfo *info;
151 struct string_list_item *author, *item; 161 struct string_list_item *author, *item;
152 struct authorstat *authorstat; 162 struct authorstat *authorstat;
153 struct string_list *items; 163 struct string_list *items;
154 char *tmp; 164 char *tmp;
@@ -187,13 +197,13 @@ static int cmp_total_commits(const void *a1, const void *a2)
187} 197}
188 198
189/* Walk the commit DAG and collect number of commits per author per 199/* Walk the commit DAG and collect number of commits per author per
190 * timeperiod into a nested string_list collection. 200 * timeperiod into a nested string_list collection.
191 */ 201 */
192struct string_list collect_stats(struct cgit_context *ctx, 202struct string_list collect_stats(struct cgit_context *ctx,
193 struct Period *period) 203 struct cgit_period *period)
194{ 204{
195 struct string_list authors; 205 struct string_list authors;
196 struct rev_info rev; 206 struct rev_info rev;
197 struct commit *commit; 207 struct commit *commit;
198 const char *argv[] = {NULL, ctx->qry.head, NULL, NULL, NULL, NULL}; 208 const char *argv[] = {NULL, ctx->qry.head, NULL, NULL, NULL, NULL};
199 int argc = 3; 209 int argc = 3;
@@ -230,13 +240,13 @@ struct string_list collect_stats(struct cgit_context *ctx,
230 } 240 }
231 return authors; 241 return authors;
232} 242}
233 243
234void print_combined_authorrow(struct string_list *authors, int from, int to, 244void print_combined_authorrow(struct string_list *authors, int from, int to,
235 const char *name, const char *leftclass, const char *centerclass, 245 const char *name, const char *leftclass, const char *centerclass,
236 const char *rightclass, struct Period *period) 246 const char *rightclass, struct cgit_period *period)
237{ 247{
238 struct string_list_item *author; 248 struct string_list_item *author;
239 struct authorstat *authorstat; 249 struct authorstat *authorstat;
240 struct string_list *items; 250 struct string_list *items;
241 struct string_list_item *date; 251 struct string_list_item *date;
242 time_t now; 252 time_t now;
@@ -268,13 +278,14 @@ void print_combined_authorrow(struct string_list *authors, int from, int to,
268 htmlf("<td class='%s'>%d</td>", centerclass, subtotal); 278 htmlf("<td class='%s'>%d</td>", centerclass, subtotal);
269 total += subtotal; 279 total += subtotal;
270 } 280 }
271 htmlf("<td class='%s'>%d</td></tr>", rightclass, total); 281 htmlf("<td class='%s'>%d</td></tr>", rightclass, total);
272} 282}
273 283
274void print_authors(struct string_list *authors, int top, struct Period *period) 284void print_authors(struct string_list *authors, int top,
285 struct cgit_period *period)
275{ 286{
276 struct string_list_item *author; 287 struct string_list_item *author;
277 struct authorstat *authorstat; 288 struct authorstat *authorstat;
278 struct string_list *items; 289 struct string_list *items;
279 struct string_list_item *date; 290 struct string_list_item *date;
280 time_t now; 291 time_t now;
@@ -336,22 +347,28 @@ void print_authors(struct string_list *authors, int top, struct Period *period)
336 * for each author is another string_list which is used to calculate the 347 * for each author is another string_list which is used to calculate the
337 * number of commits per time-interval. 348 * number of commits per time-interval.
338 */ 349 */
339void cgit_show_stats(struct cgit_context *ctx) 350void cgit_show_stats(struct cgit_context *ctx)
340{ 351{
341 struct string_list authors; 352 struct string_list authors;
342 struct Period *period; 353 struct cgit_period *period;
343 int top, i; 354 int top, i;
355 const char *code = "w";
344 356
345 period = &periods[0]; 357 if (ctx->qry.period)
346 if (ctx->qry.period) { 358 code = ctx->qry.period;
347 for (i = 0; i < sizeof(periods) / sizeof(periods[0]); i++) 359
348 if (periods[i].code == ctx->qry.period[0]) { 360 i = cgit_find_stats_period(code, &period);
349 period = &periods[i]; 361 if (!i) {
350 break; 362 cgit_print_error(fmt("Unknown statistics type: %c", code));
351 } 363 return;
364 }
365 if (i > ctx->repo->max_stats) {
366 cgit_print_error(fmt("Statistics type disabled: %s",
367 period->name));
368 return;
352 } 369 }
353 authors = collect_stats(ctx, period); 370 authors = collect_stats(ctx, period);
354 qsort(authors.items, authors.nr, sizeof(struct string_list_item), 371 qsort(authors.items, authors.nr, sizeof(struct string_list_item),
355 cmp_total_commits); 372 cmp_total_commits);
356 373
357 top = ctx->qry.ofs; 374 top = ctx->qry.ofs;
@@ -365,20 +382,22 @@ void cgit_show_stats(struct cgit_context *ctx)
365 } 382 }
366 html("</h2>"); 383 html("</h2>");
367 384
368 html("<form method='get' action='.' style='float: right; text-align: right;'>"); 385 html("<form method='get' action='.' style='float: right; text-align: right;'>");
369 if (strcmp(ctx->qry.head, ctx->repo->defbranch)) 386 if (strcmp(ctx->qry.head, ctx->repo->defbranch))
370 htmlf("<input type='hidden' name='h' value='%s'/>", ctx->qry.head); 387 htmlf("<input type='hidden' name='h' value='%s'/>", ctx->qry.head);
371 html("Period: "); 388 if (ctx->repo->max_stats > 1) {
372 html("<select name='period' onchange='this.form.submit();'>"); 389 html("Period: ");
373 for (i = 0; i < sizeof(periods) / sizeof(periods[0]); i++) 390 html("<select name='period' onchange='this.form.submit();'>");
374 htmlf("<option value='%c'%s>%s</option>", 391 for (i = 0; i < ctx->repo->max_stats; i++)
375 periods[i].code, 392 htmlf("<option value='%c'%s>%s</option>",
376 period == &periods[i] ? " selected" : "", 393 periods[i].code,
377 periods[i].name); 394 period == &periods[i] ? " selected" : "",
378 html("</select><br/><br/>"); 395 periods[i].name);
396 html("</select><br/><br/>");
397 }
379 html("Authors: "); 398 html("Authors: ");
380 html(""); 399 html("");
381 html("<select name='ofs' onchange='this.form.submit();'>"); 400 html("<select name='ofs' onchange='this.form.submit();'>");
382 htmlf("<option value='10'%s>10</option>", top == 10 ? " selected" : ""); 401 htmlf("<option value='10'%s>10</option>", top == 10 ? " selected" : "");
383 htmlf("<option value='25'%s>25</option>", top == 25 ? " selected" : ""); 402 htmlf("<option value='25'%s>25</option>", top == 25 ? " selected" : "");
384 htmlf("<option value='50'%s>50</option>", top == 50 ? " selected" : ""); 403 htmlf("<option value='50'%s>50</option>", top == 50 ? " selected" : "");