Fossil

Check-in [bb6f23313e]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add a by-hour-of-day report to the /reports page, prompted by a /chat discussion. (Edit: initial user mis-attribution was caused by a config-pull which changed my local default user.)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: bb6f23313ed6e48a322d41b210da413006e8f747d264b11a55ae59f88ba91f06
User & Date: stephan 2022-10-18 16:17:23.294
Original Comment: Add a by-hour-of-day report to the /reports page, prompted by a /chat discussion.
Original User & Date: drh 2022-10-18 16:17:23.294
Context
2022-10-18
16:55
Cache get_comment_format() result to avoid that the global config db is queried once per timeline row, which brings fossil to a standstill when ~/ is NFS-mounted. Discussion in [forum:9aaefe4e536e01bf]. ... (check-in: 6e62c8510f user: stephan tags: trunk)
16:17
Add a by-hour-of-day report to the /reports page, prompted by a /chat discussion. (Edit: initial user mis-attribution was caused by a config-pull which changed my local default user.) ... (check-in: bb6f23313e user: stephan tags: trunk)
13:35
For the BUILTIN virtual table, map one-based rowids to zero-based array indices, to fix access to the first entry. ... (check-in: cf563c7217 user: florian tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/statrep.c.
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
                                        current year */
  char showYearTotal = 0;            /* Flag telling us when to show
                                        the per-year event totals */
  int nMaxEvents  = 1;               /* for calculating length of graph
                                        bars. */
  int iterations = 0;                /* number of weeks/months we iterate
                                        over */
  Blob userFilter = empty_blob;      /* Optional user=johndoe query string */
  stats_report_init_view();
  if( zUserName ){
    blob_appendf(&userFilter, "user=%s", zUserName);
  }
  blob_reset(&userFilter);
  db_prepare(&query,
             "SELECT substr(date(mtime),1,%d) AS timeframe,"
             "       count(*) AS eventCount"
             "  FROM v_reports"
             " WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
             " GROUP BY timeframe"
             " ORDER BY timeframe DESC",







<

<
<
<
<







195
196
197
198
199
200
201

202




203
204
205
206
207
208
209
                                        current year */
  char showYearTotal = 0;            /* Flag telling us when to show
                                        the per-year event totals */
  int nMaxEvents  = 1;               /* for calculating length of graph
                                        bars. */
  int iterations = 0;                /* number of weeks/months we iterate
                                        over */

  stats_report_init_view();




  db_prepare(&query,
             "SELECT substr(date(mtime),1,%d) AS timeframe,"
             "       count(*) AS eventCount"
             "  FROM v_reports"
             " WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
             " GROUP BY timeframe"
             " ORDER BY timeframe DESC",
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
static void stats_report_day_of_week(const char *zUserName){
  Stmt query = empty_Stmt;
  int nRowNumber = 0;                /* current TR number */
  int rowClass = 0;                  /* counter for alternating
                                        row colors */
  int nMaxEvents = 1;                /* max number of events for
                                        all rows. */
  Blob userFilter = empty_blob;      /* Optional user=johndoe query string */
  static const char *const daysOfWeek[] = {
  "Sunday", "Monday", "Tuesday", "Wednesday",
  "Thursday", "Friday", "Saturday"
  };

  stats_report_init_view();
  if( zUserName ){
    blob_appendf(&userFilter, "user=%s", zUserName);
  }
  db_prepare(&query,
               "SELECT cast(strftime('%%w', mtime) AS INTEGER) dow,"
               "       COUNT(*) AS eventCount"
               "  FROM v_reports"
               " WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
               " GROUP BY dow ORDER BY dow", zUserName);
  @ <h1>Timeline Events (%h(stats_report_label_for_type())) by Day of the Week







<






<
<
<







470
471
472
473
474
475
476

477
478
479
480
481
482



483
484
485
486
487
488
489
static void stats_report_day_of_week(const char *zUserName){
  Stmt query = empty_Stmt;
  int nRowNumber = 0;                /* current TR number */
  int rowClass = 0;                  /* counter for alternating
                                        row colors */
  int nMaxEvents = 1;                /* max number of events for
                                        all rows. */

  static const char *const daysOfWeek[] = {
  "Sunday", "Monday", "Tuesday", "Wednesday",
  "Thursday", "Friday", "Saturday"
  };

  stats_report_init_view();



  db_prepare(&query,
               "SELECT cast(strftime('%%w', mtime) AS INTEGER) dow,"
               "       COUNT(*) AS eventCount"
               "  FROM v_reports"
               " WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
               " GROUP BY dow ORDER BY dow", zUserName);
  @ <h1>Timeline Events (%h(stats_report_label_for_type())) by Day of the Week
548
549
550
551
552
553
554











































































555
556
557
558
559
560
561
    if(!nCount) continue /* arguable! Possible? */;
    else if(!nSize) nSize = 1;
    rowClass = ++nRowNumber % 2;
    @<tr class='row%d(rowClass)'>
    @ <td>%d(dayNum)</td>
    @ <td>%s(daysOfWeek[dayNum])</td>
    @ <td>%d(nCount)</td>











































































    @ <td>
    @ <div class='statistics-report-graph-line'
    @  style='width:%d(nSize)%%;'>&nbsp;</div>
    @ </td>
    @</tr>
  }
  @ </tbody></table>







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
    if(!nCount) continue /* arguable! Possible? */;
    else if(!nSize) nSize = 1;
    rowClass = ++nRowNumber % 2;
    @<tr class='row%d(rowClass)'>
    @ <td>%d(dayNum)</td>
    @ <td>%s(daysOfWeek[dayNum])</td>
    @ <td>%d(nCount)</td>
    @ <td>
    @ <div class='statistics-report-graph-line'
    @  style='width:%d(nSize)%%;'>&nbsp;</div>
    @ </td>
    @</tr>
  }
  @ </tbody></table>
  db_finalize(&query);
}

/*
** Implements the "byhour" view for /reports. If zUserName is not NULL
** then the report is restricted to events created by the named user
** account.
*/
static void stats_report_hour_of_day(const char *zUserName){
  Stmt query = empty_Stmt;
  int nRowNumber = 0;                /* current TR number */
  int rowClass = 0;                  /* counter for alternating
                                        row colors */
  int nMaxEvents = 1;                /* max number of events for
                                        all rows. */

  stats_report_init_view();
  db_prepare(&query,
               "SELECT cast(strftime('%%H', mtime) AS INTEGER) hod,"
               "       COUNT(*) AS eventCount"
               "  FROM v_reports"
               " WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
               " GROUP BY hod ORDER BY hod", zUserName);
  @ <h1>Timeline Events (%h(stats_report_label_for_type())) by Hour of Day
  if( zUserName ){
    @ for user %h(zUserName)
  }
  @ </h1>
  db_multi_exec(
    "CREATE TEMP VIEW piechart(amt,label) AS"
    " SELECT count(*), strftime('%%H', mtime) hod"
    "  FROM v_reports"
    "  WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
    "  GROUP BY 2 ORDER BY hod;",
    zUserName
  );
  if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){
    @ <center><svg width=700 height=400>
    piechart_render(700, 400, PIE_OTHER|PIE_PERCENT);
    @ </svg></centre><hr />
  }
  style_table_sorter();
  @ <table class='statistics-report-table-events sortable' border='0' \
  @ cellpadding='2' cellspacing='0' data-column-types='nnx' data-init-sort='1'>
  @ <thead><tr>
  @ <th>Hour</th>
  @ <th>Events</th>
  @ <th width='90%%'><!-- relative commits graph --></th>
  @ </tr></thead><tbody>
  while( SQLITE_ROW == db_step(&query) ){
    const int nCount = db_column_int(&query, 1);
    if(nCount>nMaxEvents){
      nMaxEvents = nCount;
    }
  }
  db_reset(&query);
  while( SQLITE_ROW == db_step(&query) ){
    const int hourNum =db_column_int(&query, 0);
    const int nCount = db_column_int(&query, 1);
    int nSize = nCount
      ? (int)(100 * nCount / nMaxEvents)
      : 0;
    if(!nCount) continue /* arguable! Possible? */;
    else if(!nSize) nSize = 1;
    rowClass = ++nRowNumber % 2;
    @<tr class='row%d(rowClass)'>
    @ <td>%d(hourNum)</td>
    @ <td>%d(nCount)</td>
    @ <td>
    @ <div class='statistics-report-graph-line'
    @  style='width:%d(nSize)%%;'>&nbsp;</div>
    @ </td>
    @</tr>
  }
  @ </tbody></table>
707
708
709
710
711
712
713

714
715
716
717
718
719
720
#define RPT_BYFILE    1
#define RPT_BYMONTH   2
#define RPT_BYUSER    3
#define RPT_BYWEEK    4
#define RPT_BYWEEKDAY 5
#define RPT_BYYEAR    6
#define RPT_LASTCHNG  7  /* Last change made for each user */

#define RPT_NONE      0  /* None of the above */

/*
** WEBPAGE: reports
**
** Shows activity reports for the repository.
**







>







773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
#define RPT_BYFILE    1
#define RPT_BYMONTH   2
#define RPT_BYUSER    3
#define RPT_BYWEEK    4
#define RPT_BYWEEKDAY 5
#define RPT_BYYEAR    6
#define RPT_LASTCHNG  7  /* Last change made for each user */
#define RPT_BYHOUR    8  /* hour-of-day */
#define RPT_NONE      0  /* None of the above */

/*
** WEBPAGE: reports
**
** Shows activity reports for the repository.
**
747
748
749
750
751
752
753

754
755
756
757
758
759
760
     {  "File Changes","byfile",    RPT_BYFILE    },
     {  "Last Change", "lastchng",  RPT_LASTCHNG  },
     {  "By Month",    "bymonth",   RPT_BYMONTH   },
     {  "By User",     "byuser",    RPT_BYUSER    },
     {  "By Week",     "byweek",    RPT_BYWEEK    },
     {  "By Weekday",  "byweekday", RPT_BYWEEKDAY },
     {  "By Year",     "byyear",    RPT_BYYEAR    },

  };
  static const char *const azType[] = {
     "a",  "All Changes",
     "ci", "Check-ins",
     "f",  "Forum Posts",
     "g",  "Tags",
     "e",  "Tech Notes",







>







814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
     {  "File Changes","byfile",    RPT_BYFILE    },
     {  "Last Change", "lastchng",  RPT_LASTCHNG  },
     {  "By Month",    "bymonth",   RPT_BYMONTH   },
     {  "By User",     "byuser",    RPT_BYUSER    },
     {  "By Week",     "byweek",    RPT_BYWEEK    },
     {  "By Weekday",  "byweekday", RPT_BYWEEKDAY },
     {  "By Year",     "byyear",    RPT_BYYEAR    },
     {  "By Hour",     "byhour",    RPT_BYHOUR    },
  };
  static const char *const azType[] = {
     "a",  "All Changes",
     "ci", "Check-ins",
     "f",  "Forum Posts",
     "g",  "Tags",
     "e",  "Tech Notes",
815
816
817
818
819
820
821



822
823
824
825
826
827
      break;
    case RPT_BYWEEKDAY:
      stats_report_day_of_week(zUserName);
      break;
    case RPT_BYFILE:
      stats_report_by_file(zUserName);
      break;



    case RPT_LASTCHNG:
      stats_report_last_change();
      break;
  }
  style_finish_page();
}







>
>
>






883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
      break;
    case RPT_BYWEEKDAY:
      stats_report_day_of_week(zUserName);
      break;
    case RPT_BYFILE:
      stats_report_by_file(zUserName);
      break;
    case RPT_BYHOUR:
      stats_report_hour_of_day(zUserName);
      break;
    case RPT_LASTCHNG:
      stats_report_last_change();
      break;
  }
  style_finish_page();
}
Changes to www/changes.wiki.
9
10
11
12
13
14
15

16
17
18
19
20
21
22
  *  More elements of the /info page are now inside of an accordion.
  *  Replace the <tt>--dryrun</tt> flag with <tt>--dry-run</tt> in all
     commands which still used the former name, for consistency.
  *  Rebuilt [/file/Dockerfile | the stock Dockerfile] to create a "from scratch"
     Busybox based container image via an Alpine Linux intermediary
  *  Added [/doc/trunk/www/containers.md | a new document] describing how to
     customize, use, and run that container.


<h2 id='v2_19'>Changes for version 2.19 (2022-07-21)</h2>
  *  On file listing pages, sort filenames using the "uintnocase" collating
     sequence, so that filenames that contains embedded integers sort in
     numeric order even if they contain a different number of digits.
     (Example:  "fossil_80_..." comes before "fossil_100.png" in the
     [/dir?ci=92fd091703a28c07&name=skins/blitz|/skins/blitz] directory listing.)







>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  *  More elements of the /info page are now inside of an accordion.
  *  Replace the <tt>--dryrun</tt> flag with <tt>--dry-run</tt> in all
     commands which still used the former name, for consistency.
  *  Rebuilt [/file/Dockerfile | the stock Dockerfile] to create a "from scratch"
     Busybox based container image via an Alpine Linux intermediary
  *  Added [/doc/trunk/www/containers.md | a new document] describing how to
     customize, use, and run that container.
  *  Added "by hour of day" report to [/reports?view=byhour|the /reports page].

<h2 id='v2_19'>Changes for version 2.19 (2022-07-21)</h2>
  *  On file listing pages, sort filenames using the "uintnocase" collating
     sequence, so that filenames that contains embedded integers sort in
     numeric order even if they contain a different number of digits.
     (Example:  "fossil_80_..." comes before "fossil_100.png" in the
     [/dir?ci=92fd091703a28c07&name=skins/blitz|/skins/blitz] directory listing.)