Fossil SCM

Improvements to byweek and byday reports.

drh 2026-06-30 17:25 UTC trunk
Commit c810af8dab8607ec3d6c57be265246037ba1fb64baea8f7b0d78d5ea7c49de5c
3 files changed +220 -154 +2 -1 +3 -1
+220 -154
--- src/statrep.c
+++ src/statrep.c
@@ -166,11 +166,11 @@
166166
case 'e':
167167
return "technotes";
168168
case 'f':
169169
return "forum posts";
170170
case 'h':
171
- return "forum threads";
171
+ return "new forum threads";
172172
case 'w':
173173
return "wiki changes";
174174
case 't':
175175
return "ticket changes";
176176
case 'g':
@@ -671,99 +671,254 @@
671671
**
672672
** If zUserName is not NULL then the report is restricted to events
673673
** created by the named user account.
674674
*/
675675
static void stats_report_year_weeks(const char *zUserName){
676
- const char *zYear = P("y"); /* Year for which report shown */
676
+ const char *zYear = P("y"); /* Year for which report shown */
677
+ int bShowAll = PB("sa"); /* Show all weeks if true, active if false */
678
+ char *zLimit; /* Date of last week to show */
679
+ Stmt q;
680
+ int nMaxEvents = 1; /* max number of events for all rows. */
681
+ int rowCount = 0;
682
+ int total;
683
+ int iCurrentWeek; /* Current week number */
684
+ double rNowFraction = 0.0; /* Fraction of current week that has
685
+ ** passed */
686
+
687
+ stats_report_init_view();
688
+ style_submenu_sql("y", "Year:",
689
+ "WITH RECURSIVE a(b) AS ("
690
+ " SELECT substr(date('now'),1,4) UNION ALL"
691
+ " SELECT b-1 FROM a\n"
692
+ " WHERE b>0+(SELECT substr(date(min(mtime)),1,4) FROM event)"
693
+ ") SELECT b, b FROM a ORDER BY b DESC"
694
+ );
695
+ style_submenu_checkbox("sa", "Show-All", 0, 0);
696
+ if( zYear==0 || strlen(zYear)!=4 ){
697
+ zYear = db_text("1970","SELECT substr(date('now'),1,4);");
698
+ }
699
+ zLimit = db_text("1971-01-01",
700
+ "SELECT min(date('%q-01-01','+1 year','-1 day'),date())",
701
+ zYear
702
+ );
703
+ db_multi_exec(
704
+ "CREATE TEMP TABLE wkdata(wk,n);\n"
705
+ "WITH RECURSIVE c(wkn) AS (\n"
706
+ " VALUES(0)\n"
707
+ " UNION ALL\n"
708
+ " SELECT wkn+1 FROM c\n"
709
+ " WHERE date('%q-01-01',format('%%+d days',wkn*7+7))<=%Q\n"
710
+ ")\n"
711
+ "INSERT INTO wkdata(wk,n)\n"
712
+ " SELECT c.wkn, coalesce(x.n,0)\n"
713
+ " FROM c LEFT JOIN (\n"
714
+ " SELECT 0+strftime('%%W',mtime) AS w,\n"
715
+ " count(*) AS n \n"
716
+ " FROM v_reports\n"
717
+ " WHERE mtime BETWEEN julianday('%q-01-01 00:00:00')\n"
718
+ " AND julianday(%Q)\n"
719
+ " AND ifnull(coalesce(euser,user,'')=%Q,1)\n"
720
+ " GROUP BY w) AS x ON c.wkn=x.w;\n",
721
+ zYear, zLimit,
722
+ zYear, zLimit, zUserName
723
+ );
724
+ cgi_printf("<br>\n");
725
+ @ <h1>Timeline events (%h(stats_report_label_for_type()))
726
+ @ for the calendar weeks of %h(zYear)
727
+ if( zUserName ){
728
+ @ for user %h(zUserName)
729
+ }
730
+ @ </h1>
731
+ iCurrentWeek = db_int(0,
732
+ "SELECT strftime('%%W','now') WHERE date() LIKE '%q-%%'", zYear);
733
+ if( iCurrentWeek>0 ){
734
+ rNowFraction = db_double(0.5,
735
+ "SELECT (unixepoch()-unixepoch('now','weekday 0','-7 days'))/604800.0;");
736
+ }
737
+ style_table_sorter();
738
+ cgi_printf("<table class='statistics-report-table-events sortable' "
739
+ "border='0' cellpadding='2' width='100%%' "
740
+ "cellspacing='0' data-column-types='tnx' data-init-sort='0'>\n");
741
+ cgi_printf("<thead><tr>"
742
+ "<th>Week</th>"
743
+ "<th>Events</th>"
744
+ "<th width='90%%'><!-- relative commits graph --></th>"
745
+ "</tr></thead>\n"
746
+ "<tbody>\n");
747
+ nMaxEvents = db_int(0, "SELECT max(n) FROM wkdata");
748
+ db_prepare(&q, "SELECT wk, n FROM wkdata ORDER BY wk DESC");
749
+ while( SQLITE_ROW == db_step(&q) ){
750
+ const int iWeek = db_column_int(&q,0);
751
+ const int nCount = db_column_int(&q,1);
752
+ int nSize = (nCount>0 && nMaxEvents>0)
753
+ ? (int)(100 * nCount / nMaxEvents)
754
+ : 0;
755
+ if( nCount==0 && !bShowAll ) continue;
756
+ if(!nSize) nSize = 1;
757
+ cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
758
+ cgi_printf("<td><a href='%R/timeline?yw=%t%02d&y=%s",
759
+ zYear, iWeek,
760
+ statsReportTimelineYFlag);
761
+ if( zUserName ){
762
+ cgi_printf("&u=%t",zUserName);
763
+ }
764
+ cgi_printf("'>%02d</a></td>",iWeek);
765
+
766
+ cgi_printf("<td>%d</td>",nCount);
767
+ cgi_printf("<td style='white-space: nowrap;'>");
768
+ if( nCount ){
769
+ if( iCurrentWeek==iWeek
770
+ && rNowFraction>0.05
771
+ && nMaxEvents>0
772
+ ){
773
+ /* If the timespan covered by this row contains "now", then project
774
+ ** the number of changes until the completion of the week and
775
+ ** show a dashed box of that projection. */
776
+ int nProj = (int)(((double)nCount)/rNowFraction);
777
+ int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
778
+ int nXSize = (100 * nExtra)/nMaxEvents;
779
+ @ <span class='statistics-report-graph-line' \
780
+ @ style='display:inline-block;min-width:%d(nSize)%%;'>&nbsp;</span>\
781
+ @ <span class='statistics-report-graph-extra' title='%d(nProj)' \
782
+ @ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
783
+ }else{
784
+ @ <div class='statistics-report-graph-line' \
785
+ @ style='width:%d(nSize)%%;'>&nbsp;</div> \
786
+ }
787
+ }
788
+ cgi_printf("</td></tr>\n");
789
+ }
790
+ db_finalize(&q);
791
+ cgi_printf("</tbody></table>\n<br>\n<div\n");
792
+ total = db_int(0, "SELECT sum(n) FROM wkdata");
793
+ cgi_printf("Total events: %d<br>\n", total);
794
+ if( total ){
795
+ cgi_printf("Weeks covered: %d<br>\n",
796
+ db_int(0, "SELECT count(*) FROM wkdata"));
797
+ cgi_printf("Average events per week: %.1f<br>\n",
798
+ db_double(0.0, "SELECT avg(n) FROM wkdata"));
799
+ cgi_printf("Median events per week: %.1f<br>\n",
800
+ db_double(0.0, "SELECT median(n) FROM wkdata"));
801
+ cgi_printf("Active weeks: %d<br>\n",
802
+ db_int(0, "SELECT count(*) FROM wkdata WHERE n>0"));
803
+ cgi_printf("Average events per active week: %.1f<br>\n",
804
+ db_double(0.0, "SELECT avg(n) FROM wkdata WHERE n>0"));
805
+ cgi_printf("Median events per active week: %.1f<br>\n",
806
+ db_double(0.0, "SELECT median(n) FROM wkdata WHERE n>0"));
807
+ }
808
+}
809
+
810
+/*
811
+** Generator for the by-day report (RPT_BYDAY).
812
+**
813
+** The "y" query parameter is the year in format YYYY.
814
+**
815
+** If zUserName is not NULL then the report is restricted to events
816
+** created by the named user account.
817
+*/
818
+static void stats_report_year_days(const char *zUserName){
819
+ const char *zYear = P("y"); /* Year for which report shown */
820
+ int bShowAll = PB("sa"); /* Show all days if true, active if false */
821
+ char *zLimit; /* Date of last day to show */
677822
Stmt q;
678
- int nMaxEvents = 1; /* max number of events for
679
- all rows. */
680
- int iterations = 0; /* # of active time periods. */
823
+ int nMaxEvents = 1; /* max number of events for all rows. */
681824
int rowCount = 0;
682
- int total = 0;
683
- char *zCurrentWeek; /* Current week number */
684
- double rNowFraction = 0.0; /* Fraction of current week that has
685
- ** passed */
825
+ int total;
826
+ char *zCurrentDay; /* Current week number */
827
+ double rNowFraction = 0.0; /* Fraction of current day that has
828
+ ** passed */
686829
687830
stats_report_init_view();
688831
style_submenu_sql("y", "Year:",
689832
"WITH RECURSIVE a(b) AS ("
690833
" SELECT substr(date('now'),1,4) UNION ALL"
691
- " SELECT b-1 FROM a"
834
+ " SELECT b-1 FROM a\n"
692835
" WHERE b>0+(SELECT substr(date(min(mtime)),1,4) FROM event)"
693
- ") SELECT b, b FROM a ORDER BY b DESC");
836
+ ") SELECT b, b FROM a ORDER BY b DESC"
837
+ );
838
+ style_submenu_checkbox("sa", "Show-All", 0, 0);
694839
if( zYear==0 || strlen(zYear)!=4 ){
695840
zYear = db_text("1970","SELECT substr(date('now'),1,4);");
696841
}
842
+ zLimit = db_text("1971-01-01",
843
+ "SELECT min(date('%q-01-01','+1 year','-1 day'),date())",
844
+ zYear
845
+ );
846
+ db_multi_exec(
847
+ "CREATE TEMP TABLE daydata(dn,isodate,dow,n);\n"
848
+ "WITH RECURSIVE c(daynum, isodate, dow) AS (\n"
849
+ " VALUES(0,'%q-01-01',0+strftime('%%w','%q-01-01'))\n"
850
+ " UNION ALL\n"
851
+ " SELECT daynum+1, date(isodate,'+1 day'), (dow+1)%%6\n"
852
+ " FROM c\n"
853
+ " WHERE isodate<%Q\n"
854
+ ")\n"
855
+ "INSERT INTO daydata(dn,isodate,dow,n)\n"
856
+ " SELECT c.daynum, c.isodate, c.dow, coalesce(x.n,0)\n"
857
+ " FROM c LEFT JOIN (\n"
858
+ " SELECT date(mtime) AS edate, count(*) AS n\n"
859
+ " FROM v_reports\n"
860
+ " WHERE mtime BETWEEN julianday('%q-01-01 00:00:00')\n"
861
+ " AND julianday(%Q)\n"
862
+ " AND ifnull(coalesce(euser,user,'')=%Q,1)\n"
863
+ " GROUP BY edate) AS x ON c.isodate=x.edate;\n",
864
+ zYear, zYear,
865
+ zLimit,
866
+ zYear,
867
+ zLimit,
868
+ zUserName
869
+ );
697870
cgi_printf("<br>\n");
698
- db_prepare(&q,
699
- "SELECT DISTINCT strftime('%%W',mtime) AS wk, "
700
- " count(*) AS n "
701
- " FROM v_reports "
702
- " WHERE %Q=substr(date(mtime),1,4) "
703
- " AND mtime < current_timestamp "
704
- " AND ifnull(coalesce(euser,user,'')=%Q,1)"
705
- " GROUP BY wk ORDER BY wk DESC", zYear, zUserName);
706871
@ <h1>Timeline events (%h(stats_report_label_for_type()))
707872
@ for the calendar weeks of %h(zYear)
708873
if( zUserName ){
709874
@ for user %h(zUserName)
710875
}
711876
@ </h1>
712
- zCurrentWeek = db_text(0,
713
- "SELECT strftime('%%W','now') WHERE date() LIKE '%q%%'",
714
- zYear);
715
- if( zCurrentWeek ){
877
+ zCurrentDay = db_text(0, "SELECT date()");
878
+ if( zCurrentDay ){
716879
rNowFraction = db_double(0.5,
717
- "SELECT (unixepoch()-unixepoch('now','weekday 0','-7 days'))/604800.0;");
880
+ "SELECT (unixepoch()-unixepoch('now','start of day'))/604800.0;");
718881
}
719
- style_table_sorter();
882
+ style_table_sorter();
720883
cgi_printf("<table class='statistics-report-table-events sortable' "
721884
"border='0' cellpadding='2' width='100%%' "
722885
"cellspacing='0' data-column-types='tnx' data-init-sort='0'>\n");
723886
cgi_printf("<thead><tr>"
724887
"<th>Week</th>"
725888
"<th>Events</th>"
726889
"<th width='90%%'><!-- relative commits graph --></th>"
727890
"</tr></thead>\n"
728891
"<tbody>\n");
729
- while( SQLITE_ROW == db_step(&q) ){
730
- int nCount = db_column_int(&q, 1);
731
- if( zCurrentWeek!=0
732
- && strcmp(db_column_text(&q,0),zCurrentWeek)==0
733
- && rNowFraction>0.05
734
- ){
735
- nCount = (int)(((double)nCount)/rNowFraction);
736
- }
737
- if(nCount>nMaxEvents){
738
- nMaxEvents = nCount;
739
- }
740
- ++iterations;
741
- }
742
- db_reset(&q);
743
- while( SQLITE_ROW == db_step(&q) ){
744
- const char *zWeek = db_column_text(&q,0);
745
- const int nCount = db_column_int(&q,1);
892
+ nMaxEvents = db_int(0, "SELECT max(n) FROM daydata");
893
+ db_prepare(&q, "SELECT isodate,dow,n FROM daydata ORDER BY dn DESC");
894
+ while( SQLITE_ROW == db_step(&q) ){
895
+ static const char *azDayName[] = {
896
+ "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat"
897
+ };
898
+ const char *zDate = db_column_text(&q,0);
899
+ int iDOW = db_column_int(&q,1);
900
+ const int nCount = db_column_int(&q,2);
746901
int nSize = (nCount>0 && nMaxEvents>0)
747902
? (int)(100 * nCount / nMaxEvents)
748903
: 0;
904
+ assert( iDOW>=0 && iDOW<=6 );
905
+ if( nCount==0 && !bShowAll ) continue;
749906
if(!nSize) nSize = 1;
750
- total += nCount;
751907
cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
752
- cgi_printf("<td><a href='%R/timeline?yw=%t%s&y=%s",
753
- zYear, zWeek,
754
- statsReportTimelineYFlag);
908
+ cgi_printf("<td><a href='%R/timeline?ymd=%s&y=%s",
909
+ zDate, statsReportTimelineYFlag);
755910
if( zUserName ){
756911
cgi_printf("&u=%t",zUserName);
757912
}
758
- cgi_printf("'>%s</a></td>",zWeek);
913
+ cgi_printf("'>%s&nbsp;(%s)</a></td>",zDate,azDayName[iDOW]);
759914
760915
cgi_printf("<td>%d</td>",nCount);
761916
cgi_printf("<td style='white-space: nowrap;'>");
762917
if( nCount ){
763
- if( zCurrentWeek!=0
764
- && strcmp(zWeek, zCurrentWeek)==0
918
+ if( zCurrentDay!=0
919
+ && strcmp(zDate, zCurrentDay)==0
765920
&& rNowFraction>0.05
766921
&& nMaxEvents>0
767922
){
768923
/* If the timespan covered by this row contains "now", then project
769924
** the number of changes until the completion of the week and
@@ -781,115 +936,26 @@
781936
}
782937
}
783938
cgi_printf("</td></tr>\n");
784939
}
785940
db_finalize(&q);
786
- cgi_printf("</tbody></table>");
787
- if(total){
788
- int nAvg = iterations ? (total/iterations) : 0;
789
- cgi_printf("<br><div>Total events: %d<br>"
790
- "Average per active week: %d</div>",
791
- total, nAvg);
792
- }
793
-}
794
-
795
-/*
796
-** Generator for the by-day report (RPT_BYDAY).
797
-**
798
-** The "y" query parameter is the year in format YYYY.
799
-**
800
-** If zUserName is not NULL then the report is restricted to events
801
-** created by the named user account.
802
-*/
803
-static void stats_report_year_days(const char *zUserName){
804
- const char *zYear = P("y"); /* Year for which report shown */
805
- Stmt q;
806
- int nMaxEvents = 1; /* max number of events for
807
- all rows. */
808
- int iterations = 0; /* # of active time periods. */
809
- int rowCount = 0;
810
- int total = 0;
811
-
812
- stats_report_init_view();
813
- style_submenu_sql("y", "Year:",
814
- "WITH RECURSIVE a(b) AS ("
815
- " SELECT substr(date('now'),1,4) UNION ALL"
816
- " SELECT b-1 FROM a"
817
- " WHERE b>0+(SELECT substr(date(min(mtime)),1,4) FROM event)"
818
- ") SELECT b, b FROM a ORDER BY b DESC");
819
- if( zYear==0 || strlen(zYear)!=4 ){
820
- zYear = db_text("1970","SELECT substr(date('now'),1,4);");
821
- }
822
- cgi_printf("<br>\n");
823
- db_prepare(&q,
824
- "SELECT DISTINCT strftime('%%w',mtime) AS wkday, "
825
- " date(mtime) AS date,"
826
- " count(*) AS n "
827
- " FROM v_reports"
828
- " WHERE %Q=substr(date(mtime),1,4) "
829
- " AND mtime < current_timestamp "
830
- " AND ifnull(coalesce(euser,user,'')=%Q,1)"
831
- " GROUP BY date ORDER BY date DESC", zYear, zUserName);
832
- @ <h1>Timeline events (%h(stats_report_label_for_type()))
833
- @ for each day of %h(zYear)
834
- if( zUserName ){
835
- @ for user %h(zUserName)
836
- }
837
- @ </h1>
838
- style_table_sorter();
839
- cgi_printf("<table class='statistics-report-table-events sortable' "
840
- "border='0' cellpadding='2' width='100%%' "
841
- "cellspacing='0' data-column-types='tnx' data-init-sort='0'>\n");
842
- cgi_printf("<thead><tr>"
843
- "<th>Date</th>"
844
- "<th>Events</th>"
845
- "<th width='90%%'><!-- relative commits graph --></th>"
846
- "</tr></thead>\n"
847
- "<tbody>\n");
848
- while( SQLITE_ROW == db_step(&q) ){
849
- int nCount = db_column_int(&q, 2);
850
- if(nCount>nMaxEvents){
851
- nMaxEvents = nCount;
852
- }
853
- ++iterations;
854
- }
855
- db_reset(&q);
856
- while( SQLITE_ROW == db_step(&q) ){
857
- static const char *azDayName[] = {
858
- "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat"
859
- };
860
- int dayOfWeek = db_column_int(&q,0);
861
- const char *zDate = db_column_text(&q,1);
862
- const int nCount = db_column_int(&q,2);
863
- int nSize = (nCount>0 && nMaxEvents>0)
864
- ? (int)(100 * nCount / nMaxEvents)
865
- : 0;
866
- if(!nSize) nSize = 1;
867
- total += nCount;
868
- cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
869
- cgi_printf("<td><a href='%R/timeline?ymd=%s&y=%s",
870
- zDate, statsReportTimelineYFlag);
871
- if( zUserName ){
872
- cgi_printf("&u=%t",zUserName);
873
- }
874
- cgi_printf("'>%s&nbsp;(%s)</a></td>",zDate, azDayName[dayOfWeek]);
875
-
876
- cgi_printf("<td>%d</td>",nCount);
877
- cgi_printf("<td style='white-space: nowrap;'>");
878
- if( nCount ){
879
- @ <div class='statistics-report-graph-line' \
880
- @ style='width:%d(nSize)%%;'>&nbsp;</div> \
881
- }
882
- cgi_printf("</td></tr>\n");
883
- }
884
- db_finalize(&q);
885
- cgi_printf("</tbody></table>");
886
- if(total){
887
- int nAvg = iterations ? (total/iterations) : 0;
888
- cgi_printf("<br><div>Total events: %d<br>"
889
- "Average per active day: %d</div>",
890
- total, nAvg);
941
+ cgi_printf("</tbody></table>\n<br>\n<div\n");
942
+ total = db_int(0, "SELECT sum(n) FROM daydata");
943
+ cgi_printf("Total events: %d<br>\n", total);
944
+ if( total ){
945
+ cgi_printf("Days covered: %d<br>\n",
946
+ db_int(0, "SELECT count(*) FROM daydata"));
947
+ cgi_printf("Average events per day: %.1f<br>\n",
948
+ db_double(0.0, "SELECT avg(n) FROM daydata"));
949
+ cgi_printf("Median events per day: %.1f<br>\n",
950
+ db_double(0.0, "SELECT median(n) FROM daydata"));
951
+ cgi_printf("Active days: %d<br>\n",
952
+ db_int(0, "SELECT count(*) FROM daydata WHERE n>0"));
953
+ cgi_printf("Average events per active day: %.1f<br>\n",
954
+ db_double(0.0, "SELECT avg(n) FROM daydata WHERE n>0"));
955
+ cgi_printf("Median events per active day: %.1f<br>\n",
956
+ db_double(0.0, "SELECT median(n) FROM daydata WHERE n>0"));
891957
}
892958
}
893959
894960
895961
/*
896962
--- src/statrep.c
+++ src/statrep.c
@@ -166,11 +166,11 @@
166 case 'e':
167 return "technotes";
168 case 'f':
169 return "forum posts";
170 case 'h':
171 return "forum threads";
172 case 'w':
173 return "wiki changes";
174 case 't':
175 return "ticket changes";
176 case 'g':
@@ -671,99 +671,254 @@
671 **
672 ** If zUserName is not NULL then the report is restricted to events
673 ** created by the named user account.
674 */
675 static void stats_report_year_weeks(const char *zUserName){
676 const char *zYear = P("y"); /* Year for which report shown */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
677 Stmt q;
678 int nMaxEvents = 1; /* max number of events for
679 all rows. */
680 int iterations = 0; /* # of active time periods. */
681 int rowCount = 0;
682 int total = 0;
683 char *zCurrentWeek; /* Current week number */
684 double rNowFraction = 0.0; /* Fraction of current week that has
685 ** passed */
686
687 stats_report_init_view();
688 style_submenu_sql("y", "Year:",
689 "WITH RECURSIVE a(b) AS ("
690 " SELECT substr(date('now'),1,4) UNION ALL"
691 " SELECT b-1 FROM a"
692 " WHERE b>0+(SELECT substr(date(min(mtime)),1,4) FROM event)"
693 ") SELECT b, b FROM a ORDER BY b DESC");
 
 
694 if( zYear==0 || strlen(zYear)!=4 ){
695 zYear = db_text("1970","SELECT substr(date('now'),1,4);");
696 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
697 cgi_printf("<br>\n");
698 db_prepare(&q,
699 "SELECT DISTINCT strftime('%%W',mtime) AS wk, "
700 " count(*) AS n "
701 " FROM v_reports "
702 " WHERE %Q=substr(date(mtime),1,4) "
703 " AND mtime < current_timestamp "
704 " AND ifnull(coalesce(euser,user,'')=%Q,1)"
705 " GROUP BY wk ORDER BY wk DESC", zYear, zUserName);
706 @ <h1>Timeline events (%h(stats_report_label_for_type()))
707 @ for the calendar weeks of %h(zYear)
708 if( zUserName ){
709 @ for user %h(zUserName)
710 }
711 @ </h1>
712 zCurrentWeek = db_text(0,
713 "SELECT strftime('%%W','now') WHERE date() LIKE '%q%%'",
714 zYear);
715 if( zCurrentWeek ){
716 rNowFraction = db_double(0.5,
717 "SELECT (unixepoch()-unixepoch('now','weekday 0','-7 days'))/604800.0;");
718 }
719 style_table_sorter();
720 cgi_printf("<table class='statistics-report-table-events sortable' "
721 "border='0' cellpadding='2' width='100%%' "
722 "cellspacing='0' data-column-types='tnx' data-init-sort='0'>\n");
723 cgi_printf("<thead><tr>"
724 "<th>Week</th>"
725 "<th>Events</th>"
726 "<th width='90%%'><!-- relative commits graph --></th>"
727 "</tr></thead>\n"
728 "<tbody>\n");
729 while( SQLITE_ROW == db_step(&q) ){
730 int nCount = db_column_int(&q, 1);
731 if( zCurrentWeek!=0
732 && strcmp(db_column_text(&q,0),zCurrentWeek)==0
733 && rNowFraction>0.05
734 ){
735 nCount = (int)(((double)nCount)/rNowFraction);
736 }
737 if(nCount>nMaxEvents){
738 nMaxEvents = nCount;
739 }
740 ++iterations;
741 }
742 db_reset(&q);
743 while( SQLITE_ROW == db_step(&q) ){
744 const char *zWeek = db_column_text(&q,0);
745 const int nCount = db_column_int(&q,1);
746 int nSize = (nCount>0 && nMaxEvents>0)
747 ? (int)(100 * nCount / nMaxEvents)
748 : 0;
 
 
749 if(!nSize) nSize = 1;
750 total += nCount;
751 cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
752 cgi_printf("<td><a href='%R/timeline?yw=%t%s&y=%s",
753 zYear, zWeek,
754 statsReportTimelineYFlag);
755 if( zUserName ){
756 cgi_printf("&u=%t",zUserName);
757 }
758 cgi_printf("'>%s</a></td>",zWeek);
759
760 cgi_printf("<td>%d</td>",nCount);
761 cgi_printf("<td style='white-space: nowrap;'>");
762 if( nCount ){
763 if( zCurrentWeek!=0
764 && strcmp(zWeek, zCurrentWeek)==0
765 && rNowFraction>0.05
766 && nMaxEvents>0
767 ){
768 /* If the timespan covered by this row contains "now", then project
769 ** the number of changes until the completion of the week and
@@ -781,115 +936,26 @@
781 }
782 }
783 cgi_printf("</td></tr>\n");
784 }
785 db_finalize(&q);
786 cgi_printf("</tbody></table>");
787 if(total){
788 int nAvg = iterations ? (total/iterations) : 0;
789 cgi_printf("<br><div>Total events: %d<br>"
790 "Average per active week: %d</div>",
791 total, nAvg);
792 }
793 }
794
795 /*
796 ** Generator for the by-day report (RPT_BYDAY).
797 **
798 ** The "y" query parameter is the year in format YYYY.
799 **
800 ** If zUserName is not NULL then the report is restricted to events
801 ** created by the named user account.
802 */
803 static void stats_report_year_days(const char *zUserName){
804 const char *zYear = P("y"); /* Year for which report shown */
805 Stmt q;
806 int nMaxEvents = 1; /* max number of events for
807 all rows. */
808 int iterations = 0; /* # of active time periods. */
809 int rowCount = 0;
810 int total = 0;
811
812 stats_report_init_view();
813 style_submenu_sql("y", "Year:",
814 "WITH RECURSIVE a(b) AS ("
815 " SELECT substr(date('now'),1,4) UNION ALL"
816 " SELECT b-1 FROM a"
817 " WHERE b>0+(SELECT substr(date(min(mtime)),1,4) FROM event)"
818 ") SELECT b, b FROM a ORDER BY b DESC");
819 if( zYear==0 || strlen(zYear)!=4 ){
820 zYear = db_text("1970","SELECT substr(date('now'),1,4);");
821 }
822 cgi_printf("<br>\n");
823 db_prepare(&q,
824 "SELECT DISTINCT strftime('%%w',mtime) AS wkday, "
825 " date(mtime) AS date,"
826 " count(*) AS n "
827 " FROM v_reports"
828 " WHERE %Q=substr(date(mtime),1,4) "
829 " AND mtime < current_timestamp "
830 " AND ifnull(coalesce(euser,user,'')=%Q,1)"
831 " GROUP BY date ORDER BY date DESC", zYear, zUserName);
832 @ <h1>Timeline events (%h(stats_report_label_for_type()))
833 @ for each day of %h(zYear)
834 if( zUserName ){
835 @ for user %h(zUserName)
836 }
837 @ </h1>
838 style_table_sorter();
839 cgi_printf("<table class='statistics-report-table-events sortable' "
840 "border='0' cellpadding='2' width='100%%' "
841 "cellspacing='0' data-column-types='tnx' data-init-sort='0'>\n");
842 cgi_printf("<thead><tr>"
843 "<th>Date</th>"
844 "<th>Events</th>"
845 "<th width='90%%'><!-- relative commits graph --></th>"
846 "</tr></thead>\n"
847 "<tbody>\n");
848 while( SQLITE_ROW == db_step(&q) ){
849 int nCount = db_column_int(&q, 2);
850 if(nCount>nMaxEvents){
851 nMaxEvents = nCount;
852 }
853 ++iterations;
854 }
855 db_reset(&q);
856 while( SQLITE_ROW == db_step(&q) ){
857 static const char *azDayName[] = {
858 "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat"
859 };
860 int dayOfWeek = db_column_int(&q,0);
861 const char *zDate = db_column_text(&q,1);
862 const int nCount = db_column_int(&q,2);
863 int nSize = (nCount>0 && nMaxEvents>0)
864 ? (int)(100 * nCount / nMaxEvents)
865 : 0;
866 if(!nSize) nSize = 1;
867 total += nCount;
868 cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
869 cgi_printf("<td><a href='%R/timeline?ymd=%s&y=%s",
870 zDate, statsReportTimelineYFlag);
871 if( zUserName ){
872 cgi_printf("&u=%t",zUserName);
873 }
874 cgi_printf("'>%s&nbsp;(%s)</a></td>",zDate, azDayName[dayOfWeek]);
875
876 cgi_printf("<td>%d</td>",nCount);
877 cgi_printf("<td style='white-space: nowrap;'>");
878 if( nCount ){
879 @ <div class='statistics-report-graph-line' \
880 @ style='width:%d(nSize)%%;'>&nbsp;</div> \
881 }
882 cgi_printf("</td></tr>\n");
883 }
884 db_finalize(&q);
885 cgi_printf("</tbody></table>");
886 if(total){
887 int nAvg = iterations ? (total/iterations) : 0;
888 cgi_printf("<br><div>Total events: %d<br>"
889 "Average per active day: %d</div>",
890 total, nAvg);
891 }
892 }
893
894
895 /*
896
--- src/statrep.c
+++ src/statrep.c
@@ -166,11 +166,11 @@
166 case 'e':
167 return "technotes";
168 case 'f':
169 return "forum posts";
170 case 'h':
171 return "new forum threads";
172 case 'w':
173 return "wiki changes";
174 case 't':
175 return "ticket changes";
176 case 'g':
@@ -671,99 +671,254 @@
671 **
672 ** If zUserName is not NULL then the report is restricted to events
673 ** created by the named user account.
674 */
675 static void stats_report_year_weeks(const char *zUserName){
676 const char *zYear = P("y"); /* Year for which report shown */
677 int bShowAll = PB("sa"); /* Show all weeks if true, active if false */
678 char *zLimit; /* Date of last week to show */
679 Stmt q;
680 int nMaxEvents = 1; /* max number of events for all rows. */
681 int rowCount = 0;
682 int total;
683 int iCurrentWeek; /* Current week number */
684 double rNowFraction = 0.0; /* Fraction of current week that has
685 ** passed */
686
687 stats_report_init_view();
688 style_submenu_sql("y", "Year:",
689 "WITH RECURSIVE a(b) AS ("
690 " SELECT substr(date('now'),1,4) UNION ALL"
691 " SELECT b-1 FROM a\n"
692 " WHERE b>0+(SELECT substr(date(min(mtime)),1,4) FROM event)"
693 ") SELECT b, b FROM a ORDER BY b DESC"
694 );
695 style_submenu_checkbox("sa", "Show-All", 0, 0);
696 if( zYear==0 || strlen(zYear)!=4 ){
697 zYear = db_text("1970","SELECT substr(date('now'),1,4);");
698 }
699 zLimit = db_text("1971-01-01",
700 "SELECT min(date('%q-01-01','+1 year','-1 day'),date())",
701 zYear
702 );
703 db_multi_exec(
704 "CREATE TEMP TABLE wkdata(wk,n);\n"
705 "WITH RECURSIVE c(wkn) AS (\n"
706 " VALUES(0)\n"
707 " UNION ALL\n"
708 " SELECT wkn+1 FROM c\n"
709 " WHERE date('%q-01-01',format('%%+d days',wkn*7+7))<=%Q\n"
710 ")\n"
711 "INSERT INTO wkdata(wk,n)\n"
712 " SELECT c.wkn, coalesce(x.n,0)\n"
713 " FROM c LEFT JOIN (\n"
714 " SELECT 0+strftime('%%W',mtime) AS w,\n"
715 " count(*) AS n \n"
716 " FROM v_reports\n"
717 " WHERE mtime BETWEEN julianday('%q-01-01 00:00:00')\n"
718 " AND julianday(%Q)\n"
719 " AND ifnull(coalesce(euser,user,'')=%Q,1)\n"
720 " GROUP BY w) AS x ON c.wkn=x.w;\n",
721 zYear, zLimit,
722 zYear, zLimit, zUserName
723 );
724 cgi_printf("<br>\n");
725 @ <h1>Timeline events (%h(stats_report_label_for_type()))
726 @ for the calendar weeks of %h(zYear)
727 if( zUserName ){
728 @ for user %h(zUserName)
729 }
730 @ </h1>
731 iCurrentWeek = db_int(0,
732 "SELECT strftime('%%W','now') WHERE date() LIKE '%q-%%'", zYear);
733 if( iCurrentWeek>0 ){
734 rNowFraction = db_double(0.5,
735 "SELECT (unixepoch()-unixepoch('now','weekday 0','-7 days'))/604800.0;");
736 }
737 style_table_sorter();
738 cgi_printf("<table class='statistics-report-table-events sortable' "
739 "border='0' cellpadding='2' width='100%%' "
740 "cellspacing='0' data-column-types='tnx' data-init-sort='0'>\n");
741 cgi_printf("<thead><tr>"
742 "<th>Week</th>"
743 "<th>Events</th>"
744 "<th width='90%%'><!-- relative commits graph --></th>"
745 "</tr></thead>\n"
746 "<tbody>\n");
747 nMaxEvents = db_int(0, "SELECT max(n) FROM wkdata");
748 db_prepare(&q, "SELECT wk, n FROM wkdata ORDER BY wk DESC");
749 while( SQLITE_ROW == db_step(&q) ){
750 const int iWeek = db_column_int(&q,0);
751 const int nCount = db_column_int(&q,1);
752 int nSize = (nCount>0 && nMaxEvents>0)
753 ? (int)(100 * nCount / nMaxEvents)
754 : 0;
755 if( nCount==0 && !bShowAll ) continue;
756 if(!nSize) nSize = 1;
757 cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
758 cgi_printf("<td><a href='%R/timeline?yw=%t%02d&y=%s",
759 zYear, iWeek,
760 statsReportTimelineYFlag);
761 if( zUserName ){
762 cgi_printf("&u=%t",zUserName);
763 }
764 cgi_printf("'>%02d</a></td>",iWeek);
765
766 cgi_printf("<td>%d</td>",nCount);
767 cgi_printf("<td style='white-space: nowrap;'>");
768 if( nCount ){
769 if( iCurrentWeek==iWeek
770 && rNowFraction>0.05
771 && nMaxEvents>0
772 ){
773 /* If the timespan covered by this row contains "now", then project
774 ** the number of changes until the completion of the week and
775 ** show a dashed box of that projection. */
776 int nProj = (int)(((double)nCount)/rNowFraction);
777 int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
778 int nXSize = (100 * nExtra)/nMaxEvents;
779 @ <span class='statistics-report-graph-line' \
780 @ style='display:inline-block;min-width:%d(nSize)%%;'>&nbsp;</span>\
781 @ <span class='statistics-report-graph-extra' title='%d(nProj)' \
782 @ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
783 }else{
784 @ <div class='statistics-report-graph-line' \
785 @ style='width:%d(nSize)%%;'>&nbsp;</div> \
786 }
787 }
788 cgi_printf("</td></tr>\n");
789 }
790 db_finalize(&q);
791 cgi_printf("</tbody></table>\n<br>\n<div\n");
792 total = db_int(0, "SELECT sum(n) FROM wkdata");
793 cgi_printf("Total events: %d<br>\n", total);
794 if( total ){
795 cgi_printf("Weeks covered: %d<br>\n",
796 db_int(0, "SELECT count(*) FROM wkdata"));
797 cgi_printf("Average events per week: %.1f<br>\n",
798 db_double(0.0, "SELECT avg(n) FROM wkdata"));
799 cgi_printf("Median events per week: %.1f<br>\n",
800 db_double(0.0, "SELECT median(n) FROM wkdata"));
801 cgi_printf("Active weeks: %d<br>\n",
802 db_int(0, "SELECT count(*) FROM wkdata WHERE n>0"));
803 cgi_printf("Average events per active week: %.1f<br>\n",
804 db_double(0.0, "SELECT avg(n) FROM wkdata WHERE n>0"));
805 cgi_printf("Median events per active week: %.1f<br>\n",
806 db_double(0.0, "SELECT median(n) FROM wkdata WHERE n>0"));
807 }
808 }
809
810 /*
811 ** Generator for the by-day report (RPT_BYDAY).
812 **
813 ** The "y" query parameter is the year in format YYYY.
814 **
815 ** If zUserName is not NULL then the report is restricted to events
816 ** created by the named user account.
817 */
818 static void stats_report_year_days(const char *zUserName){
819 const char *zYear = P("y"); /* Year for which report shown */
820 int bShowAll = PB("sa"); /* Show all days if true, active if false */
821 char *zLimit; /* Date of last day to show */
822 Stmt q;
823 int nMaxEvents = 1; /* max number of events for all rows. */
 
 
824 int rowCount = 0;
825 int total;
826 char *zCurrentDay; /* Current week number */
827 double rNowFraction = 0.0; /* Fraction of current day that has
828 ** passed */
829
830 stats_report_init_view();
831 style_submenu_sql("y", "Year:",
832 "WITH RECURSIVE a(b) AS ("
833 " SELECT substr(date('now'),1,4) UNION ALL"
834 " SELECT b-1 FROM a\n"
835 " WHERE b>0+(SELECT substr(date(min(mtime)),1,4) FROM event)"
836 ") SELECT b, b FROM a ORDER BY b DESC"
837 );
838 style_submenu_checkbox("sa", "Show-All", 0, 0);
839 if( zYear==0 || strlen(zYear)!=4 ){
840 zYear = db_text("1970","SELECT substr(date('now'),1,4);");
841 }
842 zLimit = db_text("1971-01-01",
843 "SELECT min(date('%q-01-01','+1 year','-1 day'),date())",
844 zYear
845 );
846 db_multi_exec(
847 "CREATE TEMP TABLE daydata(dn,isodate,dow,n);\n"
848 "WITH RECURSIVE c(daynum, isodate, dow) AS (\n"
849 " VALUES(0,'%q-01-01',0+strftime('%%w','%q-01-01'))\n"
850 " UNION ALL\n"
851 " SELECT daynum+1, date(isodate,'+1 day'), (dow+1)%%6\n"
852 " FROM c\n"
853 " WHERE isodate<%Q\n"
854 ")\n"
855 "INSERT INTO daydata(dn,isodate,dow,n)\n"
856 " SELECT c.daynum, c.isodate, c.dow, coalesce(x.n,0)\n"
857 " FROM c LEFT JOIN (\n"
858 " SELECT date(mtime) AS edate, count(*) AS n\n"
859 " FROM v_reports\n"
860 " WHERE mtime BETWEEN julianday('%q-01-01 00:00:00')\n"
861 " AND julianday(%Q)\n"
862 " AND ifnull(coalesce(euser,user,'')=%Q,1)\n"
863 " GROUP BY edate) AS x ON c.isodate=x.edate;\n",
864 zYear, zYear,
865 zLimit,
866 zYear,
867 zLimit,
868 zUserName
869 );
870 cgi_printf("<br>\n");
 
 
 
 
 
 
 
 
871 @ <h1>Timeline events (%h(stats_report_label_for_type()))
872 @ for the calendar weeks of %h(zYear)
873 if( zUserName ){
874 @ for user %h(zUserName)
875 }
876 @ </h1>
877 zCurrentDay = db_text(0, "SELECT date()");
878 if( zCurrentDay ){
 
 
879 rNowFraction = db_double(0.5,
880 "SELECT (unixepoch()-unixepoch('now','start of day'))/604800.0;");
881 }
882 style_table_sorter();
883 cgi_printf("<table class='statistics-report-table-events sortable' "
884 "border='0' cellpadding='2' width='100%%' "
885 "cellspacing='0' data-column-types='tnx' data-init-sort='0'>\n");
886 cgi_printf("<thead><tr>"
887 "<th>Week</th>"
888 "<th>Events</th>"
889 "<th width='90%%'><!-- relative commits graph --></th>"
890 "</tr></thead>\n"
891 "<tbody>\n");
892 nMaxEvents = db_int(0, "SELECT max(n) FROM daydata");
893 db_prepare(&q, "SELECT isodate,dow,n FROM daydata ORDER BY dn DESC");
894 while( SQLITE_ROW == db_step(&q) ){
895 static const char *azDayName[] = {
896 "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat"
897 };
898 const char *zDate = db_column_text(&q,0);
899 int iDOW = db_column_int(&q,1);
900 const int nCount = db_column_int(&q,2);
 
 
 
 
 
 
 
 
901 int nSize = (nCount>0 && nMaxEvents>0)
902 ? (int)(100 * nCount / nMaxEvents)
903 : 0;
904 assert( iDOW>=0 && iDOW<=6 );
905 if( nCount==0 && !bShowAll ) continue;
906 if(!nSize) nSize = 1;
 
907 cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
908 cgi_printf("<td><a href='%R/timeline?ymd=%s&y=%s",
909 zDate, statsReportTimelineYFlag);
 
910 if( zUserName ){
911 cgi_printf("&u=%t",zUserName);
912 }
913 cgi_printf("'>%s&nbsp;(%s)</a></td>",zDate,azDayName[iDOW]);
914
915 cgi_printf("<td>%d</td>",nCount);
916 cgi_printf("<td style='white-space: nowrap;'>");
917 if( nCount ){
918 if( zCurrentDay!=0
919 && strcmp(zDate, zCurrentDay)==0
920 && rNowFraction>0.05
921 && nMaxEvents>0
922 ){
923 /* If the timespan covered by this row contains "now", then project
924 ** the number of changes until the completion of the week and
@@ -781,115 +936,26 @@
936 }
937 }
938 cgi_printf("</td></tr>\n");
939 }
940 db_finalize(&q);
941 cgi_printf("</tbody></table>\n<br>\n<div\n");
942 total = db_int(0, "SELECT sum(n) FROM daydata");
943 cgi_printf("Total events: %d<br>\n", total);
944 if( total ){
945 cgi_printf("Days covered: %d<br>\n",
946 db_int(0, "SELECT count(*) FROM daydata"));
947 cgi_printf("Average events per day: %.1f<br>\n",
948 db_double(0.0, "SELECT avg(n) FROM daydata"));
949 cgi_printf("Median events per day: %.1f<br>\n",
950 db_double(0.0, "SELECT median(n) FROM daydata"));
951 cgi_printf("Active days: %d<br>\n",
952 db_int(0, "SELECT count(*) FROM daydata WHERE n>0"));
953 cgi_printf("Average events per active day: %.1f<br>\n",
954 db_double(0.0, "SELECT avg(n) FROM daydata WHERE n>0"));
955 cgi_printf("Median events per active day: %.1f<br>\n",
956 db_double(0.0, "SELECT median(n) FROM daydata WHERE n>0"));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
957 }
958 }
959
960
961 /*
962
+2 -1
--- src/style.c
+++ src/style.c
@@ -1024,11 +1024,12 @@
10241024
** "sml" stands for submenu link.
10251025
*/
10261026
if( p->zLink==0 ){
10271027
@ <span class="label sml-%s(zClass)">%h(p->zLabel)</span>
10281028
}else{
1029
- @ <a class="label sml-%s(zClass)" href="%h(p->zLink)">%h(p->zLabel)</a>
1029
+ @ <a class="label sml-%s(zClass)" \
1030
+ @ href="%h(p->zLink)">%h(p->zLabel)</a>
10301031
}
10311032
}
10321033
}
10331034
fossil_strcpy(zClass,"smc-"); /* common prefix for submenu controls */
10341035
for(i=0; i<nSubmenuCtrl; i++){
10351036
--- src/style.c
+++ src/style.c
@@ -1024,11 +1024,12 @@
1024 ** "sml" stands for submenu link.
1025 */
1026 if( p->zLink==0 ){
1027 @ <span class="label sml-%s(zClass)">%h(p->zLabel)</span>
1028 }else{
1029 @ <a class="label sml-%s(zClass)" href="%h(p->zLink)">%h(p->zLabel)</a>
 
1030 }
1031 }
1032 }
1033 fossil_strcpy(zClass,"smc-"); /* common prefix for submenu controls */
1034 for(i=0; i<nSubmenuCtrl; i++){
1035
--- src/style.c
+++ src/style.c
@@ -1024,11 +1024,12 @@
1024 ** "sml" stands for submenu link.
1025 */
1026 if( p->zLink==0 ){
1027 @ <span class="label sml-%s(zClass)">%h(p->zLabel)</span>
1028 }else{
1029 @ <a class="label sml-%s(zClass)" \
1030 @ href="%h(p->zLink)">%h(p->zLabel)</a>
1031 }
1032 }
1033 }
1034 fossil_strcpy(zClass,"smc-"); /* common prefix for submenu controls */
1035 for(i=0; i<nSubmenuCtrl; i++){
1036
+3 -1
--- src/timeline.c
+++ src/timeline.c
@@ -1267,11 +1267,11 @@
12671267
** set the y= parameter that determines which elements to display
12681268
** on the timeline.
12691269
*/
12701270
static void timeline_y_submenu(int isDisabled){
12711271
static int i = 0;
1272
- static const char *az[16];
1272
+ static const char *az[20];
12731273
if( i==0 ){
12741274
az[0] = "all";
12751275
az[1] = "Any Type";
12761276
i = 2;
12771277
if( g.perm.Read ){
@@ -1295,10 +1295,12 @@
12951295
az[i++] = "Wiki";
12961296
}
12971297
if( g.perm.RdForum ){
12981298
az[i++] = "f";
12991299
az[i++] = "Forum";
1300
+ az[i++] = "h";
1301
+ az[i++] = "New Threads";
13001302
}
13011303
assert( i<=count(az) );
13021304
}
13031305
if( i>2 ){
13041306
style_submenu_multichoice("y", i/2, az, isDisabled);
13051307
--- src/timeline.c
+++ src/timeline.c
@@ -1267,11 +1267,11 @@
1267 ** set the y= parameter that determines which elements to display
1268 ** on the timeline.
1269 */
1270 static void timeline_y_submenu(int isDisabled){
1271 static int i = 0;
1272 static const char *az[16];
1273 if( i==0 ){
1274 az[0] = "all";
1275 az[1] = "Any Type";
1276 i = 2;
1277 if( g.perm.Read ){
@@ -1295,10 +1295,12 @@
1295 az[i++] = "Wiki";
1296 }
1297 if( g.perm.RdForum ){
1298 az[i++] = "f";
1299 az[i++] = "Forum";
 
 
1300 }
1301 assert( i<=count(az) );
1302 }
1303 if( i>2 ){
1304 style_submenu_multichoice("y", i/2, az, isDisabled);
1305
--- src/timeline.c
+++ src/timeline.c
@@ -1267,11 +1267,11 @@
1267 ** set the y= parameter that determines which elements to display
1268 ** on the timeline.
1269 */
1270 static void timeline_y_submenu(int isDisabled){
1271 static int i = 0;
1272 static const char *az[20];
1273 if( i==0 ){
1274 az[0] = "all";
1275 az[1] = "Any Type";
1276 i = 2;
1277 if( g.perm.Read ){
@@ -1295,10 +1295,12 @@
1295 az[i++] = "Wiki";
1296 }
1297 if( g.perm.RdForum ){
1298 az[i++] = "f";
1299 az[i++] = "Forum";
1300 az[i++] = "h";
1301 az[i++] = "New Threads";
1302 }
1303 assert( i<=count(az) );
1304 }
1305 if( i>2 ){
1306 style_submenu_multichoice("y", i/2, az, isDisabled);
1307

Keyboard Shortcuts

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