Fossil SCM

Move the "Activity Report" controls into drop-down menus.

drh 2015-05-18 19:19 UTC andygoth-user-reports
Commit 4d60469f15833c6d2962a90762a556228e7acd6d
1 file changed +191 -226
+191 -226
--- src/statrep.c
+++ src/statrep.c
@@ -141,63 +141,10 @@
141141
default:
142142
return "all types";
143143
}
144144
}
145145
146
-/*
147
-** A helper for the /reports family of pages which prints out a menu
148
-** of links for the various type=XXX flags. zCurrentViewName must be
149
-** the name/value of the 'view' parameter which is in effect at the
150
-** time this is called. e.g. if called from the 'byuser' view then
151
-** zCurrentViewName must be "byuser". Any URL parameters which need to
152
-** be added to the generated URLs should be passed in zParam. The
153
-** caller is expected to have already encoded any zParam in the %T or
154
-** %t encoding. */
155
-static void stats_report_event_types_menu(const char *zCurrentViewName,
156
- const char *zParam){
157
- char *zTop;
158
- if(zParam && !*zParam){
159
- zParam = NULL;
160
- }
161
- zTop = mprintf("%s/reports?view=%s%s%s", g.zTop, zCurrentViewName,
162
- zParam&&*zParam ? "&" : "", zParam);
163
- cgi_printf("<div>");
164
- cgi_printf("<span>Types:</span> ");
165
- if('*' == statsReportType){
166
- cgi_printf(" <strong>all</strong>", zTop);
167
- }else{
168
- cgi_printf(" <a href='%s'>all</a>", zTop);
169
- }
170
- if('c' == statsReportType){
171
- cgi_printf(" <strong>check-ins</strong>", zTop);
172
- }else{
173
- cgi_printf(" <a href='%s&type=ci'>check-ins</a>", zTop);
174
- }
175
- if('e' == statsReportType){
176
- cgi_printf(" <strong>technotes</strong>", zTop);
177
- }else{
178
- cgi_printf(" <a href='%s&type=e'>technotes</a>", zTop);
179
- }
180
- if( 't' == statsReportType ){
181
- cgi_printf(" <strong>tickets</strong>", zTop);
182
- }else{
183
- cgi_printf(" <a href='%s&type=t'>tickets</a>", zTop);
184
- }
185
- if( 'g' == statsReportType ){
186
- cgi_printf(" <strong>tags</strong>", zTop);
187
- }else{
188
- cgi_printf(" <a href='%s&type=g'>tags</a>", zTop);
189
- }
190
- if( 'w' == statsReportType ){
191
- cgi_printf(" <strong>wiki</strong>", zTop);
192
- }else{
193
- cgi_printf(" <a href='%s&type=w'>wiki</a>", zTop);
194
- }
195
- fossil_free(zTop);
196
- cgi_printf("</div>");
197
-}
198
-
199146
200147
/*
201148
** Helper for stats_report_by_month_year(), which generates a list of
202149
** week numbers. zTimeframe should be either a timeframe in the form YYYY
203150
** or YYYY-MM.
@@ -255,12 +202,10 @@
255202
Blob userFilter = empty_blob; /* Optional user=johndoe query string */
256203
stats_report_init_view();
257204
if( zUserName ){
258205
blob_appendf(&userFilter, "user=%s", zUserName);
259206
}
260
- stats_report_event_types_menu( includeMonth ? "bymonth" : "byyear",
261
- blob_str(&userFilter) );
262207
blob_reset(&userFilter);
263208
db_prepare(&query,
264209
"SELECT substr(date(mtime),1,%d) AS timeframe,"
265210
" count(*) AS eventCount"
266211
" FROM v_reports"
@@ -401,11 +346,10 @@
401346
int rowClass = 0; /* counter for alternating
402347
row colors */
403348
int nMaxEvents = 1; /* max number of events for
404349
all rows. */
405350
stats_report_init_view();
406
- stats_report_event_types_menu("byuser", NULL);
407351
@ <h1>Timeline Events
408352
@ (%s(stats_report_label_for_type())) by User</h1>
409353
db_multi_exec(
410354
"CREATE TEMP TABLE piechart(amt,label);"
411355
"INSERT INTO piechart SELECT count(*), ifnull(euser,user) FROM v_reports"
@@ -436,20 +380,21 @@
436380
}
437381
db_reset(&query);
438382
while( SQLITE_ROW == db_step(&query) ){
439383
const char *zUser = db_column_text(&query, 0);
440384
const int nCount = db_column_int(&query, 1);
385
+ char y = (char)statsReportType;
441386
int nSize = nCount
442387
? (int)(100 * nCount / nMaxEvents)
443388
: 0;
444389
if(!nCount) continue /* arguable! Possible? */;
445390
else if(!nSize) nSize = 1;
446391
rowClass = ++nRowNumber % 2;
447392
nEventTotal += nCount;
448
- @<tr class='row%d(rowClass)'>
393
+ @ <tr class='row%d(rowClass)'>
449394
@ <td>
450
- @ <a href="?view=bymonth&user=%h(zUser)&type=%c((char)statsReportType)">%h(zUser)</a>
395
+ @ <a href="?view=bymonth&user=%h(zUser)&type=%c(y)">%h(zUser)</a>
451396
@ </td><td data-sortkey='%08x(-nCount)'>%d(nCount)</td>
452397
@ <td>
453398
@ <div class='statistics-report-graph-line'
454399
@ style='width:%d(nSize)%%;'>&nbsp;</div>
455400
@ </td>
@@ -540,11 +485,10 @@
540485
541486
stats_report_init_view();
542487
if( zUserName ){
543488
blob_appendf(&userFilter, "user=%s", zUserName);
544489
}
545
- stats_report_event_types_menu("byweekday", blob_str(&userFilter));
546490
db_prepare(&query,
547491
"SELECT cast(mtime %% 7 AS INTEGER) dow,"
548492
" COUNT(*) AS eventCount"
549493
" FROM v_reports"
550494
" WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
@@ -554,16 +498,22 @@
554498
@ for user %h(zUserName)
555499
}
556500
@ </h1>
557501
db_multi_exec(
558502
"CREATE TEMP TABLE piechart(amt,label);"
559
- "INSERT INTO piechart SELECT count(*), cast(mtime %% 7 AS INT) FROM v_reports"
560
- " WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
561
- " GROUP BY 2 ORDER BY 2;"
562
- "UPDATE piechart SET label = CASE label WHEN 0 THEN 'Monday' WHEN 1 THEN 'Tuesday'"
563
- " WHEN 2 THEN 'Wednesday' WHEN 3 THEN 'Thursday' WHEN 4 THEN 'Friday'"
564
- " WHEN 5 THEN 'Saturday' ELSE 'Sunday' END;", zUserName
503
+ "INSERT INTO piechart"
504
+ " SELECT count(*), cast(mtime %% 7 AS INT) FROM v_reports"
505
+ " WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
506
+ " GROUP BY 2 ORDER BY 2;"
507
+ "UPDATE piechart SET label = CASE label"
508
+ " WHEN 0 THEN 'Monday'"
509
+ " WHEN 1 THEN 'Tuesday'"
510
+ " WHEN 2 THEN 'Wednesday'"
511
+ " WHEN 3 THEN 'Thursday'"
512
+ " WHEN 4 THEN 'Friday'"
513
+ " WHEN 5 THEN 'Saturday'"
514
+ " ELSE 'Sunday' END;", zUserName
565515
);
566516
if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){
567517
@ <center><svg width=700 height=400>
568518
piechart_render(700, 400, PIE_OTHER|PIE_PERCENT);
569519
@ </svg></centre><hr/>
@@ -614,127 +564,116 @@
614564
** week numbers. zTimeframe should be either a timeframe in the form YYYY
615565
** or YYYY-MM. If zUserName is not NULL then the report is restricted to events
616566
** created by the named user account.
617567
*/
618568
static void stats_report_year_weeks(const char *zUserName){
619
- const char *zYear = P("y");
620
- int nYear = zYear ? strlen(zYear) : 0;
569
+ const char *zYear = P("y"); /* Year for which report shown */
570
+ int isValidYear = 0; /* True if a valid year */
621571
int i = 0;
622
- Stmt qYears = empty_Stmt;
623
- char *zDefaultYear = NULL;
572
+ Stmt q;
624573
int nMaxEvents = 1; /* max number of events for
625574
all rows. */
626575
int iterations = 0; /* # of active time periods. */
627
- Blob urlParams = empty_blob;
576
+ int n = 0; /* Number of entries in azYear */
577
+ char **azYear = 0; /* Year dropdown menu */
578
+ int rowCount = 0;
579
+ int total = 0;
580
+
628581
stats_report_init_view();
629
- if(4==nYear){
630
- blob_appendf(&urlParams, "y=%T", zYear);
631
- }
632
- if( zUserName ){
633
- blob_appendf(&urlParams, "%suser=%s", blob_size(&urlParams) ? "&" : "",
634
- zUserName);
635
- }
636
- stats_report_event_types_menu("byweek", blob_str(&urlParams));
637
- blob_reset(&urlParams);
638
- db_prepare(&qYears,
582
+ db_prepare(&q,
639583
"SELECT DISTINCT substr(date(mtime),1,4) AS y"
640584
" FROM v_reports"
641585
" WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
642
- " GROUP BY y ORDER BY y", zUserName);
643
- cgi_printf("Select year: ");
644
- while( SQLITE_ROW == db_step(&qYears) ){
645
- const char *zT = db_column_text(&qYears, 0);
646
- if( i++ ){
647
- cgi_printf(" ");
648
- }
649
- cgi_printf("<a href='?view=byweek&y=%s&type=%c", zT,
650
- (char)statsReportType);
651
- if( zUserName ){
652
- cgi_printf("&user=%t",zUserName);
653
- }
654
- cgi_printf("'>%s</a>",zT);
655
- }
656
- db_finalize(&qYears);
657
- cgi_printf("<br/>");
658
- if(!zYear || !*zYear){
659
- zDefaultYear = db_text("????", "SELECT strftime('%%Y')");
660
- zYear = zDefaultYear;
661
- nYear = 4;
662
- }
663
- if(4 == nYear){
664
- Stmt stWeek = empty_Stmt;
665
- int rowCount = 0;
666
- int total = 0;
667
- db_prepare(&stWeek,
668
- "SELECT DISTINCT strftime('%%W',mtime) AS wk, "
669
- " count(*) AS n "
670
- " FROM v_reports "
671
- " WHERE %Q=substr(date(mtime),1,4) "
672
- " AND mtime < current_timestamp "
673
- " AND ifnull(coalesce(euser,user,'')=%Q,1)"
674
- " GROUP BY wk ORDER BY wk DESC", zYear, zUserName);
675
- @ <h1>Timeline events (%h(stats_report_label_for_type()))
676
- @ for the calendar weeks of %h(zYear)
677
- if( zUserName ){
678
- @ for user %h(zUserName)
679
- }
680
- @ </h1>
681
- cgi_printf("<table class='statistics-report-table-events' "
682
- "border='0' cellpadding='2' width='100%%' "
683
- "cellspacing='0' id='statsTable'>");
684
- cgi_printf("<thead><tr>"
685
- "<th>Week</th>"
686
- "<th>Events</th>"
687
- "<th width='90%%'><!-- relative commits graph --></th>"
688
- "</tr></thead>"
689
- "<tbody>");
690
- while( SQLITE_ROW == db_step(&stWeek) ){
691
- const int nCount = db_column_int(&stWeek, 1);
692
- if(nCount>nMaxEvents){
693
- nMaxEvents = nCount;
694
- }
695
- ++iterations;
696
- }
697
- db_reset(&stWeek);
698
- while( SQLITE_ROW == db_step(&stWeek) ){
699
- const char *zWeek = db_column_text(&stWeek,0);
700
- const int nCount = db_column_int(&stWeek,1);
701
- int nSize = nCount
702
- ? (int)(100 * nCount / nMaxEvents)
703
- : 0;
704
- if(!nSize) nSize = 1;
705
- total += nCount;
706
- cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
707
- cgi_printf("<td><a href='%R/timeline?yw=%t-%s&n=%d&y=%s",
708
- zYear, zWeek, nCount,
709
- statsReportTimelineYFlag);
710
- if( zUserName ){
711
- cgi_printf("&u=%t",zUserName);
712
- }
713
- cgi_printf("'>%s</a></td>",zWeek);
714
-
715
- cgi_printf("<td>%d</td>",nCount);
716
- cgi_printf("<td>");
717
- if(nCount){
718
- cgi_printf("<div class='statistics-report-graph-line'"
719
- "style='width:%d%%;'>&nbsp;</div>",
720
- nSize);
721
- }
722
- cgi_printf("</td></tr>");
723
- }
724
- db_finalize(&stWeek);
725
- free(zDefaultYear);
726
- cgi_printf("</tbody></table>");
727
- if(total){
728
- int nAvg = iterations ? (total/iterations) : 0;
729
- cgi_printf("<br><div>Total events: %d<br>"
730
- "Average per active week: %d</div>",
731
- total, nAvg);
732
- }
733
- output_table_sorting_javascript("statsTable","tnx",-1);
734
- }
735
-}
586
+ " GROUP BY y ORDER BY y DESC", zUserName);
587
+ while( SQLITE_ROW == db_step(&q) ){
588
+ azYear = fossil_realloc(azYear, sizeof(char*)*(n+2));
589
+ azYear[n] = fossil_strdup(db_column_text(&q,0));
590
+ azYear[n+1] = azYear[n];
591
+ if( !isValidYear && fossil_strcmp(zYear,azYear[n])==0 ) isValidYear = 1;
592
+ n += 2;
593
+ }
594
+ db_finalize(&q);
595
+ if( !isValidYear ) zYear = azYear[0];
596
+ style_submenu_multichoice("y", n/2, (const char**)azYear, 0);
597
+ cgi_printf("<br/>");
598
+ db_prepare(&q,
599
+ "SELECT DISTINCT strftime('%%W',mtime) AS wk, "
600
+ " count(*) AS n "
601
+ " FROM v_reports "
602
+ " WHERE %Q=substr(date(mtime),1,4) "
603
+ " AND mtime < current_timestamp "
604
+ " AND ifnull(coalesce(euser,user,'')=%Q,1)"
605
+ " GROUP BY wk ORDER BY wk DESC", zYear, zUserName);
606
+ @ <h1>Timeline events (%h(stats_report_label_for_type()))
607
+ @ for the calendar weeks of %h(zYear)
608
+ if( zUserName ){
609
+ @ for user %h(zUserName)
610
+ }
611
+ @ </h1>
612
+ cgi_printf("<table class='statistics-report-table-events' "
613
+ "border='0' cellpadding='2' width='100%%' "
614
+ "cellspacing='0' id='statsTable'>");
615
+ cgi_printf("<thead><tr>"
616
+ "<th>Week</th>"
617
+ "<th>Events</th>"
618
+ "<th width='90%%'><!-- relative commits graph --></th>"
619
+ "</tr></thead>"
620
+ "<tbody>");
621
+ while( SQLITE_ROW == db_step(&q) ){
622
+ const int nCount = db_column_int(&q, 1);
623
+ if(nCount>nMaxEvents){
624
+ nMaxEvents = nCount;
625
+ }
626
+ ++iterations;
627
+ }
628
+ db_reset(&q);
629
+ while( SQLITE_ROW == db_step(&q) ){
630
+ const char *zWeek = db_column_text(&q,0);
631
+ const int nCount = db_column_int(&q,1);
632
+ int nSize = nCount
633
+ ? (int)(100 * nCount / nMaxEvents)
634
+ : 0;
635
+ if(!nSize) nSize = 1;
636
+ total += nCount;
637
+ cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
638
+ cgi_printf("<td><a href='%R/timeline?yw=%t-%s&n=%d&y=%s",
639
+ zYear, zWeek, nCount,
640
+ statsReportTimelineYFlag);
641
+ if( zUserName ){
642
+ cgi_printf("&u=%t",zUserName);
643
+ }
644
+ cgi_printf("'>%s</a></td>",zWeek);
645
+
646
+ cgi_printf("<td>%d</td>",nCount);
647
+ cgi_printf("<td>");
648
+ if(nCount){
649
+ cgi_printf("<div class='statistics-report-graph-line'"
650
+ "style='width:%d%%;'>&nbsp;</div>",
651
+ nSize);
652
+ }
653
+ cgi_printf("</td></tr>");
654
+ }
655
+ db_finalize(&q);
656
+ cgi_printf("</tbody></table>");
657
+ if(total){
658
+ int nAvg = iterations ? (total/iterations) : 0;
659
+ cgi_printf("<br><div>Total events: %d<br>"
660
+ "Average per active week: %d</div>",
661
+ total, nAvg);
662
+ }
663
+ output_table_sorting_javascript("statsTable","tnx",-1);
664
+}
665
+
666
+/* Report types
667
+*/
668
+#define RPT_BYFILE 1
669
+#define RPT_BYMONTH 2
670
+#define RPT_BYUSER 3
671
+#define RPT_BYWEEK 4
672
+#define RPT_BYWEEKDAY 5
673
+#define RPT_BYYEAR 6
674
+#define RPT_NONE 0 /* None of the above */
736675
737676
/*
738677
** WEBPAGE: reports
739678
**
740679
** Shows activity reports for the repository.
@@ -754,62 +693,88 @@
754693
** y=YYYY The year to report (default is the server's
755694
** current year).
756695
*/
757696
void stats_report_page(){
758697
HQuery url; /* URL for various branch links */
759
- const char *zView = P("view"); /* Which view/report to show. */
760
- const char *zUserName = P("user");
761
- int haveU = !zUserName;
762
-
763
- login_check_credentials();
764
- if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
765
- if(!zUserName){
766
- zUserName = P("u");
767
- haveU = !!zUserName;
768
- }
769
- if(zUserName && !*zUserName){
770
- zUserName = NULL;
771
- }
772
- url_initialize(&url, "reports");
773
- if(0!=fossil_strcmp(zView,"byuser")){
774
- style_submenu_entry(haveU ? "u" : "user", "User:", 12, 0);
775
- if(zUserName){
776
- url_add_parameter(&url, haveU ? "u" : "user", zUserName);
777
- statrep_submenu(&url, "(Remove User Flag)", "view", zView,
778
- haveU ? "u" : "user");
779
- }
780
- }
781
- statrep_submenu(&url, "By Year", "view", "byyear", 0);
782
- statrep_submenu(&url, "By Month", "view", "bymonth", 0);
783
- statrep_submenu(&url, "By Week", "view", "byweek", 0);
784
- statrep_submenu(&url, "By Weekday", "view", "byweekday", 0);
785
- statrep_submenu(&url, "By User", "view", "byuser", "user");
786
- statrep_submenu(&url, "By File", "view", "byfile", "file");
698
+ const char *zView = P("view"); /* Which view/report to show. */
699
+ int eType = RPT_NONE; /* Numeric code for view/report to show */
700
+ int i; /* Loop counter */
701
+ const char *zUserName; /* Name of user */
702
+ const struct {
703
+ const char *zName; /* Name of view= screen type */
704
+ const char *zVal; /* Value of view= query parameter */
705
+ int eType; /* Corresponding RPT_* define */
706
+ } aViewType[] = {
707
+ { "By File", "byfile", RPT_BYFILE },
708
+ { "By Month", "bymonth", RPT_BYMONTH },
709
+ { "By User", "byuser", RPT_BYUSER },
710
+ { "By Week", "byweek", RPT_BYWEEK },
711
+ { "By Weekday", "byweekday", RPT_BYWEEKDAY },
712
+ { "By Year", "byyear", RPT_BYYEAR },
713
+ };
714
+ const char *azType[] = {
715
+ "a", "Any Type",
716
+ "ci", "Check-ins",
717
+ "g", "Tags",
718
+ "e", "Tech Notes",
719
+ "t", "Tickets",
720
+ "w", "Wiki"
721
+ };
722
+
723
+ login_check_credentials();
724
+ if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
725
+ zUserName = P("user");
726
+ if( zUserName==0 ) zUserName = P("u");
727
+ if( zUserName && zUserName[0]==0 ) zUserName = 0;
728
+ if( zView==0 ){
729
+ zView = "byuser";
730
+ cgi_replace_query_parameter("view","byuser");
731
+ }
732
+ for(i=0; i<ArraySize(aViewType); i++){
733
+ if( fossil_strcmp(zView, aViewType[i].zVal)==0 ){
734
+ eType = aViewType[i].eType;
735
+ break;
736
+ }
737
+ }
738
+ url_initialize(&url, "reports");
739
+ cgi_query_parameters_to_url(&url);
740
+ if( eType!=RPT_NONE ){
741
+ int nView = 0; /* Slots used in azView[] */
742
+ const char *azView[16]; /* Drop-down menu of view types */
743
+ for(i=0; i<ArraySize(aViewType); i++){
744
+ azView[nView++] = aViewType[i].zVal;
745
+ azView[nView++] = aViewType[i].zName;
746
+ }
747
+ style_submenu_multichoice("view", nView/2, azView, 0);
748
+ if( eType!=RPT_BYFILE ){
749
+ style_submenu_multichoice("type", ArraySize(azType)/2, azType, 0);
750
+ }
751
+ if( eType!=RPT_BYUSER ){
752
+ style_submenu_entry("u", "User:", 12, 0);
753
+ }
754
+ }
787755
style_submenu_element("Stats", "Stats", "%R/stat");
788756
url_reset(&url);
789757
style_header("Activity Reports");
790
- if(0==fossil_strcmp(zView,"byyear")){
791
- stats_report_by_month_year(0, 0, zUserName);
792
- }else if(0==fossil_strcmp(zView,"bymonth")){
793
- stats_report_by_month_year(1, 0, zUserName);
794
- }else if(0==fossil_strcmp(zView,"byweek")){
795
- stats_report_year_weeks(zUserName);
796
- }else if(0==fossil_strcmp(zView,"byuser")){
797
- stats_report_by_user();
798
- }else if(0==fossil_strcmp(zView,"byweekday")){
799
- stats_report_day_of_week(zUserName);
800
- }else if(0==fossil_strcmp(zView,"byfile")){
801
- stats_report_by_file(zUserName);
802
- }else{
803
- @ <h1>Activity Reports:</h1>
804
- @ <ul>
805
- @ <li>%z(href("?view=byyear"))Events by year</a></li>
806
- @ <li>%z(href("?view=bymonth"))Events by month</a></li>
807
- @ <li>%z(href("?view=byweek"))Events by calendar week</a></li>
808
- @ <li>%z(href("?view=byweekday"))Events by day of the week</a></li>
809
- @ <li>%z(href("?view=byuser"))Events by user</a></li>
810
- @ <li>%z(href("?view=byfile"))Events by file</a></li>
811
- @ </ul>
812
- }
813
-
758
+ switch( eType ){
759
+ case RPT_BYYEAR:
760
+ stats_report_by_month_year(0, 0, zUserName);
761
+ break;
762
+ case RPT_BYMONTH:
763
+ stats_report_by_month_year(1, 0, zUserName);
764
+ break;
765
+ case RPT_BYWEEK:
766
+ stats_report_year_weeks(zUserName);
767
+ break;
768
+ default:
769
+ case RPT_BYUSER:
770
+ stats_report_by_user();
771
+ break;
772
+ case RPT_BYWEEKDAY:
773
+ stats_report_day_of_week(zUserName);
774
+ break;
775
+ case RPT_BYFILE:
776
+ stats_report_by_file(zUserName);
777
+ break;
778
+ }
814779
style_footer();
815780
}
816781
--- src/statrep.c
+++ src/statrep.c
@@ -141,63 +141,10 @@
141 default:
142 return "all types";
143 }
144 }
145
146 /*
147 ** A helper for the /reports family of pages which prints out a menu
148 ** of links for the various type=XXX flags. zCurrentViewName must be
149 ** the name/value of the 'view' parameter which is in effect at the
150 ** time this is called. e.g. if called from the 'byuser' view then
151 ** zCurrentViewName must be "byuser". Any URL parameters which need to
152 ** be added to the generated URLs should be passed in zParam. The
153 ** caller is expected to have already encoded any zParam in the %T or
154 ** %t encoding. */
155 static void stats_report_event_types_menu(const char *zCurrentViewName,
156 const char *zParam){
157 char *zTop;
158 if(zParam && !*zParam){
159 zParam = NULL;
160 }
161 zTop = mprintf("%s/reports?view=%s%s%s", g.zTop, zCurrentViewName,
162 zParam&&*zParam ? "&" : "", zParam);
163 cgi_printf("<div>");
164 cgi_printf("<span>Types:</span> ");
165 if('*' == statsReportType){
166 cgi_printf(" <strong>all</strong>", zTop);
167 }else{
168 cgi_printf(" <a href='%s'>all</a>", zTop);
169 }
170 if('c' == statsReportType){
171 cgi_printf(" <strong>check-ins</strong>", zTop);
172 }else{
173 cgi_printf(" <a href='%s&type=ci'>check-ins</a>", zTop);
174 }
175 if('e' == statsReportType){
176 cgi_printf(" <strong>technotes</strong>", zTop);
177 }else{
178 cgi_printf(" <a href='%s&type=e'>technotes</a>", zTop);
179 }
180 if( 't' == statsReportType ){
181 cgi_printf(" <strong>tickets</strong>", zTop);
182 }else{
183 cgi_printf(" <a href='%s&type=t'>tickets</a>", zTop);
184 }
185 if( 'g' == statsReportType ){
186 cgi_printf(" <strong>tags</strong>", zTop);
187 }else{
188 cgi_printf(" <a href='%s&type=g'>tags</a>", zTop);
189 }
190 if( 'w' == statsReportType ){
191 cgi_printf(" <strong>wiki</strong>", zTop);
192 }else{
193 cgi_printf(" <a href='%s&type=w'>wiki</a>", zTop);
194 }
195 fossil_free(zTop);
196 cgi_printf("</div>");
197 }
198
199
200 /*
201 ** Helper for stats_report_by_month_year(), which generates a list of
202 ** week numbers. zTimeframe should be either a timeframe in the form YYYY
203 ** or YYYY-MM.
@@ -255,12 +202,10 @@
255 Blob userFilter = empty_blob; /* Optional user=johndoe query string */
256 stats_report_init_view();
257 if( zUserName ){
258 blob_appendf(&userFilter, "user=%s", zUserName);
259 }
260 stats_report_event_types_menu( includeMonth ? "bymonth" : "byyear",
261 blob_str(&userFilter) );
262 blob_reset(&userFilter);
263 db_prepare(&query,
264 "SELECT substr(date(mtime),1,%d) AS timeframe,"
265 " count(*) AS eventCount"
266 " FROM v_reports"
@@ -401,11 +346,10 @@
401 int rowClass = 0; /* counter for alternating
402 row colors */
403 int nMaxEvents = 1; /* max number of events for
404 all rows. */
405 stats_report_init_view();
406 stats_report_event_types_menu("byuser", NULL);
407 @ <h1>Timeline Events
408 @ (%s(stats_report_label_for_type())) by User</h1>
409 db_multi_exec(
410 "CREATE TEMP TABLE piechart(amt,label);"
411 "INSERT INTO piechart SELECT count(*), ifnull(euser,user) FROM v_reports"
@@ -436,20 +380,21 @@
436 }
437 db_reset(&query);
438 while( SQLITE_ROW == db_step(&query) ){
439 const char *zUser = db_column_text(&query, 0);
440 const int nCount = db_column_int(&query, 1);
 
441 int nSize = nCount
442 ? (int)(100 * nCount / nMaxEvents)
443 : 0;
444 if(!nCount) continue /* arguable! Possible? */;
445 else if(!nSize) nSize = 1;
446 rowClass = ++nRowNumber % 2;
447 nEventTotal += nCount;
448 @<tr class='row%d(rowClass)'>
449 @ <td>
450 @ <a href="?view=bymonth&user=%h(zUser)&type=%c((char)statsReportType)">%h(zUser)</a>
451 @ </td><td data-sortkey='%08x(-nCount)'>%d(nCount)</td>
452 @ <td>
453 @ <div class='statistics-report-graph-line'
454 @ style='width:%d(nSize)%%;'>&nbsp;</div>
455 @ </td>
@@ -540,11 +485,10 @@
540
541 stats_report_init_view();
542 if( zUserName ){
543 blob_appendf(&userFilter, "user=%s", zUserName);
544 }
545 stats_report_event_types_menu("byweekday", blob_str(&userFilter));
546 db_prepare(&query,
547 "SELECT cast(mtime %% 7 AS INTEGER) dow,"
548 " COUNT(*) AS eventCount"
549 " FROM v_reports"
550 " WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
@@ -554,16 +498,22 @@
554 @ for user %h(zUserName)
555 }
556 @ </h1>
557 db_multi_exec(
558 "CREATE TEMP TABLE piechart(amt,label);"
559 "INSERT INTO piechart SELECT count(*), cast(mtime %% 7 AS INT) FROM v_reports"
560 " WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
561 " GROUP BY 2 ORDER BY 2;"
562 "UPDATE piechart SET label = CASE label WHEN 0 THEN 'Monday' WHEN 1 THEN 'Tuesday'"
563 " WHEN 2 THEN 'Wednesday' WHEN 3 THEN 'Thursday' WHEN 4 THEN 'Friday'"
564 " WHEN 5 THEN 'Saturday' ELSE 'Sunday' END;", zUserName
 
 
 
 
 
 
565 );
566 if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){
567 @ <center><svg width=700 height=400>
568 piechart_render(700, 400, PIE_OTHER|PIE_PERCENT);
569 @ </svg></centre><hr/>
@@ -614,127 +564,116 @@
614 ** week numbers. zTimeframe should be either a timeframe in the form YYYY
615 ** or YYYY-MM. If zUserName is not NULL then the report is restricted to events
616 ** created by the named user account.
617 */
618 static void stats_report_year_weeks(const char *zUserName){
619 const char *zYear = P("y");
620 int nYear = zYear ? strlen(zYear) : 0;
621 int i = 0;
622 Stmt qYears = empty_Stmt;
623 char *zDefaultYear = NULL;
624 int nMaxEvents = 1; /* max number of events for
625 all rows. */
626 int iterations = 0; /* # of active time periods. */
627 Blob urlParams = empty_blob;
 
 
 
 
628 stats_report_init_view();
629 if(4==nYear){
630 blob_appendf(&urlParams, "y=%T", zYear);
631 }
632 if( zUserName ){
633 blob_appendf(&urlParams, "%suser=%s", blob_size(&urlParams) ? "&" : "",
634 zUserName);
635 }
636 stats_report_event_types_menu("byweek", blob_str(&urlParams));
637 blob_reset(&urlParams);
638 db_prepare(&qYears,
639 "SELECT DISTINCT substr(date(mtime),1,4) AS y"
640 " FROM v_reports"
641 " WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
642 " GROUP BY y ORDER BY y", zUserName);
643 cgi_printf("Select year: ");
644 while( SQLITE_ROW == db_step(&qYears) ){
645 const char *zT = db_column_text(&qYears, 0);
646 if( i++ ){
647 cgi_printf(" ");
648 }
649 cgi_printf("<a href='?view=byweek&y=%s&type=%c", zT,
650 (char)statsReportType);
651 if( zUserName ){
652 cgi_printf("&user=%t",zUserName);
653 }
654 cgi_printf("'>%s</a>",zT);
655 }
656 db_finalize(&qYears);
657 cgi_printf("<br/>");
658 if(!zYear || !*zYear){
659 zDefaultYear = db_text("????", "SELECT strftime('%%Y')");
660 zYear = zDefaultYear;
661 nYear = 4;
662 }
663 if(4 == nYear){
664 Stmt stWeek = empty_Stmt;
665 int rowCount = 0;
666 int total = 0;
667 db_prepare(&stWeek,
668 "SELECT DISTINCT strftime('%%W',mtime) AS wk, "
669 " count(*) AS n "
670 " FROM v_reports "
671 " WHERE %Q=substr(date(mtime),1,4) "
672 " AND mtime < current_timestamp "
673 " AND ifnull(coalesce(euser,user,'')=%Q,1)"
674 " GROUP BY wk ORDER BY wk DESC", zYear, zUserName);
675 @ <h1>Timeline events (%h(stats_report_label_for_type()))
676 @ for the calendar weeks of %h(zYear)
677 if( zUserName ){
678 @ for user %h(zUserName)
679 }
680 @ </h1>
681 cgi_printf("<table class='statistics-report-table-events' "
682 "border='0' cellpadding='2' width='100%%' "
683 "cellspacing='0' id='statsTable'>");
684 cgi_printf("<thead><tr>"
685 "<th>Week</th>"
686 "<th>Events</th>"
687 "<th width='90%%'><!-- relative commits graph --></th>"
688 "</tr></thead>"
689 "<tbody>");
690 while( SQLITE_ROW == db_step(&stWeek) ){
691 const int nCount = db_column_int(&stWeek, 1);
692 if(nCount>nMaxEvents){
693 nMaxEvents = nCount;
694 }
695 ++iterations;
696 }
697 db_reset(&stWeek);
698 while( SQLITE_ROW == db_step(&stWeek) ){
699 const char *zWeek = db_column_text(&stWeek,0);
700 const int nCount = db_column_int(&stWeek,1);
701 int nSize = nCount
702 ? (int)(100 * nCount / nMaxEvents)
703 : 0;
704 if(!nSize) nSize = 1;
705 total += nCount;
706 cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
707 cgi_printf("<td><a href='%R/timeline?yw=%t-%s&n=%d&y=%s",
708 zYear, zWeek, nCount,
709 statsReportTimelineYFlag);
710 if( zUserName ){
711 cgi_printf("&u=%t",zUserName);
712 }
713 cgi_printf("'>%s</a></td>",zWeek);
714
715 cgi_printf("<td>%d</td>",nCount);
716 cgi_printf("<td>");
717 if(nCount){
718 cgi_printf("<div class='statistics-report-graph-line'"
719 "style='width:%d%%;'>&nbsp;</div>",
720 nSize);
721 }
722 cgi_printf("</td></tr>");
723 }
724 db_finalize(&stWeek);
725 free(zDefaultYear);
726 cgi_printf("</tbody></table>");
727 if(total){
728 int nAvg = iterations ? (total/iterations) : 0;
729 cgi_printf("<br><div>Total events: %d<br>"
730 "Average per active week: %d</div>",
731 total, nAvg);
732 }
733 output_table_sorting_javascript("statsTable","tnx",-1);
734 }
735 }
736
737 /*
738 ** WEBPAGE: reports
739 **
740 ** Shows activity reports for the repository.
@@ -754,62 +693,88 @@
754 ** y=YYYY The year to report (default is the server's
755 ** current year).
756 */
757 void stats_report_page(){
758 HQuery url; /* URL for various branch links */
759 const char *zView = P("view"); /* Which view/report to show. */
760 const char *zUserName = P("user");
761 int haveU = !zUserName;
762
763 login_check_credentials();
764 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
765 if(!zUserName){
766 zUserName = P("u");
767 haveU = !!zUserName;
768 }
769 if(zUserName && !*zUserName){
770 zUserName = NULL;
771 }
772 url_initialize(&url, "reports");
773 if(0!=fossil_strcmp(zView,"byuser")){
774 style_submenu_entry(haveU ? "u" : "user", "User:", 12, 0);
775 if(zUserName){
776 url_add_parameter(&url, haveU ? "u" : "user", zUserName);
777 statrep_submenu(&url, "(Remove User Flag)", "view", zView,
778 haveU ? "u" : "user");
779 }
780 }
781 statrep_submenu(&url, "By Year", "view", "byyear", 0);
782 statrep_submenu(&url, "By Month", "view", "bymonth", 0);
783 statrep_submenu(&url, "By Week", "view", "byweek", 0);
784 statrep_submenu(&url, "By Weekday", "view", "byweekday", 0);
785 statrep_submenu(&url, "By User", "view", "byuser", "user");
786 statrep_submenu(&url, "By File", "view", "byfile", "file");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
787 style_submenu_element("Stats", "Stats", "%R/stat");
788 url_reset(&url);
789 style_header("Activity Reports");
790 if(0==fossil_strcmp(zView,"byyear")){
791 stats_report_by_month_year(0, 0, zUserName);
792 }else if(0==fossil_strcmp(zView,"bymonth")){
793 stats_report_by_month_year(1, 0, zUserName);
794 }else if(0==fossil_strcmp(zView,"byweek")){
795 stats_report_year_weeks(zUserName);
796 }else if(0==fossil_strcmp(zView,"byuser")){
797 stats_report_by_user();
798 }else if(0==fossil_strcmp(zView,"byweekday")){
799 stats_report_day_of_week(zUserName);
800 }else if(0==fossil_strcmp(zView,"byfile")){
801 stats_report_by_file(zUserName);
802 }else{
803 @ <h1>Activity Reports:</h1>
804 @ <ul>
805 @ <li>%z(href("?view=byyear"))Events by year</a></li>
806 @ <li>%z(href("?view=bymonth"))Events by month</a></li>
807 @ <li>%z(href("?view=byweek"))Events by calendar week</a></li>
808 @ <li>%z(href("?view=byweekday"))Events by day of the week</a></li>
809 @ <li>%z(href("?view=byuser"))Events by user</a></li>
810 @ <li>%z(href("?view=byfile"))Events by file</a></li>
811 @ </ul>
812 }
813
814 style_footer();
815 }
816
--- src/statrep.c
+++ src/statrep.c
@@ -141,63 +141,10 @@
141 default:
142 return "all types";
143 }
144 }
145
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
147 /*
148 ** Helper for stats_report_by_month_year(), which generates a list of
149 ** week numbers. zTimeframe should be either a timeframe in the form YYYY
150 ** or YYYY-MM.
@@ -255,12 +202,10 @@
202 Blob userFilter = empty_blob; /* Optional user=johndoe query string */
203 stats_report_init_view();
204 if( zUserName ){
205 blob_appendf(&userFilter, "user=%s", zUserName);
206 }
 
 
207 blob_reset(&userFilter);
208 db_prepare(&query,
209 "SELECT substr(date(mtime),1,%d) AS timeframe,"
210 " count(*) AS eventCount"
211 " FROM v_reports"
@@ -401,11 +346,10 @@
346 int rowClass = 0; /* counter for alternating
347 row colors */
348 int nMaxEvents = 1; /* max number of events for
349 all rows. */
350 stats_report_init_view();
 
351 @ <h1>Timeline Events
352 @ (%s(stats_report_label_for_type())) by User</h1>
353 db_multi_exec(
354 "CREATE TEMP TABLE piechart(amt,label);"
355 "INSERT INTO piechart SELECT count(*), ifnull(euser,user) FROM v_reports"
@@ -436,20 +380,21 @@
380 }
381 db_reset(&query);
382 while( SQLITE_ROW == db_step(&query) ){
383 const char *zUser = db_column_text(&query, 0);
384 const int nCount = db_column_int(&query, 1);
385 char y = (char)statsReportType;
386 int nSize = nCount
387 ? (int)(100 * nCount / nMaxEvents)
388 : 0;
389 if(!nCount) continue /* arguable! Possible? */;
390 else if(!nSize) nSize = 1;
391 rowClass = ++nRowNumber % 2;
392 nEventTotal += nCount;
393 @ <tr class='row%d(rowClass)'>
394 @ <td>
395 @ <a href="?view=bymonth&user=%h(zUser)&type=%c(y)">%h(zUser)</a>
396 @ </td><td data-sortkey='%08x(-nCount)'>%d(nCount)</td>
397 @ <td>
398 @ <div class='statistics-report-graph-line'
399 @ style='width:%d(nSize)%%;'>&nbsp;</div>
400 @ </td>
@@ -540,11 +485,10 @@
485
486 stats_report_init_view();
487 if( zUserName ){
488 blob_appendf(&userFilter, "user=%s", zUserName);
489 }
 
490 db_prepare(&query,
491 "SELECT cast(mtime %% 7 AS INTEGER) dow,"
492 " COUNT(*) AS eventCount"
493 " FROM v_reports"
494 " WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
@@ -554,16 +498,22 @@
498 @ for user %h(zUserName)
499 }
500 @ </h1>
501 db_multi_exec(
502 "CREATE TEMP TABLE piechart(amt,label);"
503 "INSERT INTO piechart"
504 " SELECT count(*), cast(mtime %% 7 AS INT) FROM v_reports"
505 " WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
506 " GROUP BY 2 ORDER BY 2;"
507 "UPDATE piechart SET label = CASE label"
508 " WHEN 0 THEN 'Monday'"
509 " WHEN 1 THEN 'Tuesday'"
510 " WHEN 2 THEN 'Wednesday'"
511 " WHEN 3 THEN 'Thursday'"
512 " WHEN 4 THEN 'Friday'"
513 " WHEN 5 THEN 'Saturday'"
514 " ELSE 'Sunday' END;", zUserName
515 );
516 if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){
517 @ <center><svg width=700 height=400>
518 piechart_render(700, 400, PIE_OTHER|PIE_PERCENT);
519 @ </svg></centre><hr/>
@@ -614,127 +564,116 @@
564 ** week numbers. zTimeframe should be either a timeframe in the form YYYY
565 ** or YYYY-MM. If zUserName is not NULL then the report is restricted to events
566 ** created by the named user account.
567 */
568 static void stats_report_year_weeks(const char *zUserName){
569 const char *zYear = P("y"); /* Year for which report shown */
570 int isValidYear = 0; /* True if a valid year */
571 int i = 0;
572 Stmt q;
 
573 int nMaxEvents = 1; /* max number of events for
574 all rows. */
575 int iterations = 0; /* # of active time periods. */
576 int n = 0; /* Number of entries in azYear */
577 char **azYear = 0; /* Year dropdown menu */
578 int rowCount = 0;
579 int total = 0;
580
581 stats_report_init_view();
582 db_prepare(&q,
 
 
 
 
 
 
 
 
 
583 "SELECT DISTINCT substr(date(mtime),1,4) AS y"
584 " FROM v_reports"
585 " WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
586 " GROUP BY y ORDER BY y DESC", zUserName);
587 while( SQLITE_ROW == db_step(&q) ){
588 azYear = fossil_realloc(azYear, sizeof(char*)*(n+2));
589 azYear[n] = fossil_strdup(db_column_text(&q,0));
590 azYear[n+1] = azYear[n];
591 if( !isValidYear && fossil_strcmp(zYear,azYear[n])==0 ) isValidYear = 1;
592 n += 2;
593 }
594 db_finalize(&q);
595 if( !isValidYear ) zYear = azYear[0];
596 style_submenu_multichoice("y", n/2, (const char**)azYear, 0);
597 cgi_printf("<br/>");
598 db_prepare(&q,
599 "SELECT DISTINCT strftime('%%W',mtime) AS wk, "
600 " count(*) AS n "
601 " FROM v_reports "
602 " WHERE %Q=substr(date(mtime),1,4) "
603 " AND mtime < current_timestamp "
604 " AND ifnull(coalesce(euser,user,'')=%Q,1)"
605 " GROUP BY wk ORDER BY wk DESC", zYear, zUserName);
606 @ <h1>Timeline events (%h(stats_report_label_for_type()))
607 @ for the calendar weeks of %h(zYear)
608 if( zUserName ){
609 @ for user %h(zUserName)
610 }
611 @ </h1>
612 cgi_printf("<table class='statistics-report-table-events' "
613 "border='0' cellpadding='2' width='100%%' "
614 "cellspacing='0' id='statsTable'>");
615 cgi_printf("<thead><tr>"
616 "<th>Week</th>"
617 "<th>Events</th>"
618 "<th width='90%%'><!-- relative commits graph --></th>"
619 "</tr></thead>"
620 "<tbody>");
621 while( SQLITE_ROW == db_step(&q) ){
622 const int nCount = db_column_int(&q, 1);
623 if(nCount>nMaxEvents){
624 nMaxEvents = nCount;
625 }
626 ++iterations;
627 }
628 db_reset(&q);
629 while( SQLITE_ROW == db_step(&q) ){
630 const char *zWeek = db_column_text(&q,0);
631 const int nCount = db_column_int(&q,1);
632 int nSize = nCount
633 ? (int)(100 * nCount / nMaxEvents)
634 : 0;
635 if(!nSize) nSize = 1;
636 total += nCount;
637 cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
638 cgi_printf("<td><a href='%R/timeline?yw=%t-%s&n=%d&y=%s",
639 zYear, zWeek, nCount,
640 statsReportTimelineYFlag);
641 if( zUserName ){
642 cgi_printf("&u=%t",zUserName);
643 }
644 cgi_printf("'>%s</a></td>",zWeek);
645
646 cgi_printf("<td>%d</td>",nCount);
647 cgi_printf("<td>");
648 if(nCount){
649 cgi_printf("<div class='statistics-report-graph-line'"
650 "style='width:%d%%;'>&nbsp;</div>",
651 nSize);
652 }
653 cgi_printf("</td></tr>");
654 }
655 db_finalize(&q);
656 cgi_printf("</tbody></table>");
657 if(total){
658 int nAvg = iterations ? (total/iterations) : 0;
659 cgi_printf("<br><div>Total events: %d<br>"
660 "Average per active week: %d</div>",
661 total, nAvg);
662 }
663 output_table_sorting_javascript("statsTable","tnx",-1);
664 }
665
666 /* Report types
667 */
668 #define RPT_BYFILE 1
669 #define RPT_BYMONTH 2
670 #define RPT_BYUSER 3
671 #define RPT_BYWEEK 4
672 #define RPT_BYWEEKDAY 5
673 #define RPT_BYYEAR 6
674 #define RPT_NONE 0 /* None of the above */
 
 
 
 
 
675
676 /*
677 ** WEBPAGE: reports
678 **
679 ** Shows activity reports for the repository.
@@ -754,62 +693,88 @@
693 ** y=YYYY The year to report (default is the server's
694 ** current year).
695 */
696 void stats_report_page(){
697 HQuery url; /* URL for various branch links */
698 const char *zView = P("view"); /* Which view/report to show. */
699 int eType = RPT_NONE; /* Numeric code for view/report to show */
700 int i; /* Loop counter */
701 const char *zUserName; /* Name of user */
702 const struct {
703 const char *zName; /* Name of view= screen type */
704 const char *zVal; /* Value of view= query parameter */
705 int eType; /* Corresponding RPT_* define */
706 } aViewType[] = {
707 { "By File", "byfile", RPT_BYFILE },
708 { "By Month", "bymonth", RPT_BYMONTH },
709 { "By User", "byuser", RPT_BYUSER },
710 { "By Week", "byweek", RPT_BYWEEK },
711 { "By Weekday", "byweekday", RPT_BYWEEKDAY },
712 { "By Year", "byyear", RPT_BYYEAR },
713 };
714 const char *azType[] = {
715 "a", "Any Type",
716 "ci", "Check-ins",
717 "g", "Tags",
718 "e", "Tech Notes",
719 "t", "Tickets",
720 "w", "Wiki"
721 };
722
723 login_check_credentials();
724 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
725 zUserName = P("user");
726 if( zUserName==0 ) zUserName = P("u");
727 if( zUserName && zUserName[0]==0 ) zUserName = 0;
728 if( zView==0 ){
729 zView = "byuser";
730 cgi_replace_query_parameter("view","byuser");
731 }
732 for(i=0; i<ArraySize(aViewType); i++){
733 if( fossil_strcmp(zView, aViewType[i].zVal)==0 ){
734 eType = aViewType[i].eType;
735 break;
736 }
737 }
738 url_initialize(&url, "reports");
739 cgi_query_parameters_to_url(&url);
740 if( eType!=RPT_NONE ){
741 int nView = 0; /* Slots used in azView[] */
742 const char *azView[16]; /* Drop-down menu of view types */
743 for(i=0; i<ArraySize(aViewType); i++){
744 azView[nView++] = aViewType[i].zVal;
745 azView[nView++] = aViewType[i].zName;
746 }
747 style_submenu_multichoice("view", nView/2, azView, 0);
748 if( eType!=RPT_BYFILE ){
749 style_submenu_multichoice("type", ArraySize(azType)/2, azType, 0);
750 }
751 if( eType!=RPT_BYUSER ){
752 style_submenu_entry("u", "User:", 12, 0);
753 }
754 }
755 style_submenu_element("Stats", "Stats", "%R/stat");
756 url_reset(&url);
757 style_header("Activity Reports");
758 switch( eType ){
759 case RPT_BYYEAR:
760 stats_report_by_month_year(0, 0, zUserName);
761 break;
762 case RPT_BYMONTH:
763 stats_report_by_month_year(1, 0, zUserName);
764 break;
765 case RPT_BYWEEK:
766 stats_report_year_weeks(zUserName);
767 break;
768 default:
769 case RPT_BYUSER:
770 stats_report_by_user();
771 break;
772 case RPT_BYWEEKDAY:
773 stats_report_day_of_week(zUserName);
774 break;
775 case RPT_BYFILE:
776 stats_report_by_file(zUserName);
777 break;
778 }
 
 
 
779 style_footer();
780 }
781

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button