about summary refs log tree commit diff stats
path: root/ui-stats.c
diff options
context:
space:
mode:
Diffstat (limited to 'ui-stats.c')
-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,26 +1,12 @@
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;
@@ -137,15 +123,39 @@ static char *pretty_year(struct tm *tm)
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;
@@ -190,7 +200,7 @@ static int cmp_total_commits(const void *a1, const void *a2)
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;
@@ -233,7 +243,7 @@ struct string_list collect_stats(struct cgit_context *ctx,
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;
@@ -271,7 +281,8 @@ void print_combined_authorrow(struct string_list *authors, int from, int to,
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;
@@ -339,16 +350,22 @@ void print_authors(struct string_list *authors, int top, struct Period *period)
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),
@@ -368,14 +385,16 @@ void cgit_show_stats(struct cgit_context *ctx)
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();'>");