Fossil SCM
integrated reports-by-type. Still missing some UI components, but the feature works if the user adds type=XYZ to the URL args.
Commit
358f8e361f60e50e220929cf1c8e312e53a10610
Parent
e17e2174a93a8b6…
1 file changed
+73
-5
+73
-5
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1850,10 +1850,75 @@ | ||
| 1850 | 1850 | @ <a href="%s(g.zTop)/timeline?p=%S(zUuid)&d=%S(zUuid)">%S(zUuid)</a> |
| 1851 | 1851 | } |
| 1852 | 1852 | db_finalize(&q); |
| 1853 | 1853 | style_footer(); |
| 1854 | 1854 | } |
| 1855 | + | |
| 1856 | +/* | |
| 1857 | +** Creates a TEMP VIEW named v_reports which is a wrapper around the | |
| 1858 | +** EVENT table filtered on event.type. It looks for the request | |
| 1859 | +** parameter 'type' (reminder: we "should" use 'y' for consistency | |
| 1860 | +** with /timeline, but /reports uses 'y' for the year) and expects it | |
| 1861 | +** to contain one of the conventional values from event.type or the | |
| 1862 | +** value "all", which is treated as equivalent to "*". By default (if | |
| 1863 | +** no 'y' is specified), "*" is assumed (that is also the default for | |
| 1864 | +** invalid/unknown filter values). That 'y' filter is the one used for | |
| 1865 | +** the event list. Note that a filter of "*" or "all" is equivalent to | |
| 1866 | +** querying against the full event table. The view, however, adds an | |
| 1867 | +** abstraction level to simplify the implementation code for the | |
| 1868 | +** various /reports pages. | |
| 1869 | +** | |
| 1870 | +** Returns one of: 'c', 'w', 'g', 't', 'e', representing the type of | |
| 1871 | +** filter it applies, or '*' if no filter is applied (i.e. if "all" is | |
| 1872 | +** used). | |
| 1873 | +*/ | |
| 1874 | +static char stats_report_init_view(){ | |
| 1875 | + char const * zType = PD("type","*"); /* analog to /timeline?y=... */ | |
| 1876 | + char const * zRealType = NULL; /* normalized form of zType */ | |
| 1877 | + char rc = 0; /* result code */ | |
| 1878 | + switch( (zType && *zType) ? *zType : 0 ){ | |
| 1879 | + case 'c': | |
| 1880 | + case 'C': | |
| 1881 | + zRealType = "ci"; | |
| 1882 | + rc = *zRealType; | |
| 1883 | + break; | |
| 1884 | + case 'e': | |
| 1885 | + case 'E': | |
| 1886 | + zRealType = "e"; | |
| 1887 | + rc = *zRealType; | |
| 1888 | + break; | |
| 1889 | + case 'g': | |
| 1890 | + case 'G': | |
| 1891 | + zRealType = "g"; | |
| 1892 | + rc = *zRealType; | |
| 1893 | + break; | |
| 1894 | + case 't': | |
| 1895 | + case 'T': | |
| 1896 | + zRealType = "t"; | |
| 1897 | + rc = *zRealType; | |
| 1898 | + break; | |
| 1899 | + case 'w': | |
| 1900 | + case 'W': | |
| 1901 | + zRealType = "w"; | |
| 1902 | + rc = *zRealType; | |
| 1903 | + break; | |
| 1904 | + default: | |
| 1905 | + rc = '*'; | |
| 1906 | + break; | |
| 1907 | + } | |
| 1908 | + assert(0 != rc); | |
| 1909 | + if(zRealType){ | |
| 1910 | + db_multi_exec("CREATE TEMP VIEW v_reports AS " | |
| 1911 | + "SELECT * FROM event WHERE type GLOB %Q", | |
| 1912 | + zRealType); | |
| 1913 | + }else{ | |
| 1914 | + db_multi_exec("CREATE TEMP VIEW v_reports AS " | |
| 1915 | + "SELECT * FROM event"); | |
| 1916 | + } | |
| 1917 | + return rc; | |
| 1918 | +} | |
| 1919 | + | |
| 1855 | 1920 | |
| 1856 | 1921 | /* |
| 1857 | 1922 | ** Helper for stats_report_by_month_year(), which generates a list of |
| 1858 | 1923 | ** week numbers. zTimeframe should be either a timeframe in the form YYYY |
| 1859 | 1924 | ** or YYYY-MM. |
| @@ -1864,11 +1929,11 @@ | ||
| 1864 | 1929 | memcpy(yearPart, zTimeframe, 4); |
| 1865 | 1930 | db_prepare(&stWeek, |
| 1866 | 1931 | "SELECT DISTINCT strftime('%%W',mtime) AS wk, " |
| 1867 | 1932 | "count(*) AS n, " |
| 1868 | 1933 | "substr(date(mtime),1,%d) AS ym " |
| 1869 | - "FROM event " | |
| 1934 | + "FROM v_reports " | |
| 1870 | 1935 | "WHERE ym=%Q AND mtime < current_timestamp " |
| 1871 | 1936 | "GROUP BY wk ORDER BY wk", |
| 1872 | 1937 | strlen(zTimeframe), |
| 1873 | 1938 | zTimeframe); |
| 1874 | 1939 | while( SQLITE_ROW == db_step(&stWeek) ){ |
| @@ -1908,16 +1973,17 @@ | ||
| 1908 | 1973 | Blob header = empty_blob; /* Page header text */ |
| 1909 | 1974 | int nMaxEvents = 1; /* for calculating length of graph |
| 1910 | 1975 | bars. */ |
| 1911 | 1976 | int iterations = 0; /* number of weeks/months we iterate |
| 1912 | 1977 | over */ |
| 1978 | + stats_report_init_view(); | |
| 1913 | 1979 | blob_appendf(&header, "Timeline Events by year%s", |
| 1914 | 1980 | (includeMonth ? "/month" : "")); |
| 1915 | 1981 | blob_appendf(&sql, |
| 1916 | 1982 | "SELECT substr(date(mtime),1,%d) AS timeframe, " |
| 1917 | 1983 | "count(*) AS eventCount " |
| 1918 | - "FROM event ", | |
| 1984 | + "FROM v_reports ", | |
| 1919 | 1985 | includeMonth ? 7 : 4); |
| 1920 | 1986 | if(zUserName&&*zUserName){ |
| 1921 | 1987 | blob_appendf(&sql, " WHERE user=%Q ", zUserName); |
| 1922 | 1988 | blob_appendf(&header," for user %q", zUserName); |
| 1923 | 1989 | } |
| @@ -2053,14 +2119,15 @@ | ||
| 2053 | 2119 | int rowClass = 0; /* counter for alternating |
| 2054 | 2120 | row colors */ |
| 2055 | 2121 | Blob sql = empty_blob; /* SQL */ |
| 2056 | 2122 | int nMaxEvents = 1; /* max number of events for |
| 2057 | 2123 | all rows. */ |
| 2124 | + stats_report_init_view(); | |
| 2058 | 2125 | blob_append(&sql, |
| 2059 | 2126 | "SELECT user, " |
| 2060 | 2127 | "COUNT(*) AS eventCount " |
| 2061 | - "FROM event " | |
| 2128 | + "FROM v_reports " | |
| 2062 | 2129 | "GROUP BY user ORDER BY eventCount DESC", |
| 2063 | 2130 | -1); |
| 2064 | 2131 | db_prepare(&query, blob_str(&sql)); |
| 2065 | 2132 | blob_reset(&sql); |
| 2066 | 2133 | @ <h1>Timeline Events by User</h1> |
| @@ -2121,14 +2188,15 @@ | ||
| 2121 | 2188 | Blob sql = empty_blob; |
| 2122 | 2189 | int nMaxEvents = 1; /* max number of events for |
| 2123 | 2190 | all rows. */ |
| 2124 | 2191 | int iterations = 0; /* # of active time periods. */ |
| 2125 | 2192 | |
| 2193 | + stats_report_init_view(); | |
| 2126 | 2194 | cgi_printf("Select year: "); |
| 2127 | 2195 | blob_append(&sql, |
| 2128 | 2196 | "SELECT DISTINCT substr(date(mtime),1,4) AS y " |
| 2129 | - "FROM event WHERE 1 ", -1); | |
| 2197 | + "FROM v_reports WHERE 1 ", -1); | |
| 2130 | 2198 | if(zUserName&&*zUserName){ |
| 2131 | 2199 | blob_appendf(&sql,"AND user=%Q ", zUserName); |
| 2132 | 2200 | } |
| 2133 | 2201 | blob_append(&sql,"GROUP BY y ORDER BY y", -1); |
| 2134 | 2202 | db_prepare(&qYears, blob_str(&sql)); |
| @@ -2159,11 +2227,11 @@ | ||
| 2159 | 2227 | blob_appendf(&header, "Timeline events for the calendar weeks " |
| 2160 | 2228 | "of %h", zYear); |
| 2161 | 2229 | blob_appendf(&sql, |
| 2162 | 2230 | "SELECT DISTINCT strftime('%%%%W',mtime) AS wk, " |
| 2163 | 2231 | "count(*) AS n " |
| 2164 | - "FROM event " | |
| 2232 | + "FROM v_reports " | |
| 2165 | 2233 | "WHERE %Q=substr(date(mtime),1,4) " |
| 2166 | 2234 | "AND mtime < current_timestamp ", |
| 2167 | 2235 | zYear); |
| 2168 | 2236 | if(zUserName&&*zUserName){ |
| 2169 | 2237 | blob_appendf(&sql, " AND user=%Q ", zUserName); |
| 2170 | 2238 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1850,10 +1850,75 @@ | |
| 1850 | @ <a href="%s(g.zTop)/timeline?p=%S(zUuid)&d=%S(zUuid)">%S(zUuid)</a> |
| 1851 | } |
| 1852 | db_finalize(&q); |
| 1853 | style_footer(); |
| 1854 | } |
| 1855 | |
| 1856 | /* |
| 1857 | ** Helper for stats_report_by_month_year(), which generates a list of |
| 1858 | ** week numbers. zTimeframe should be either a timeframe in the form YYYY |
| 1859 | ** or YYYY-MM. |
| @@ -1864,11 +1929,11 @@ | |
| 1864 | memcpy(yearPart, zTimeframe, 4); |
| 1865 | db_prepare(&stWeek, |
| 1866 | "SELECT DISTINCT strftime('%%W',mtime) AS wk, " |
| 1867 | "count(*) AS n, " |
| 1868 | "substr(date(mtime),1,%d) AS ym " |
| 1869 | "FROM event " |
| 1870 | "WHERE ym=%Q AND mtime < current_timestamp " |
| 1871 | "GROUP BY wk ORDER BY wk", |
| 1872 | strlen(zTimeframe), |
| 1873 | zTimeframe); |
| 1874 | while( SQLITE_ROW == db_step(&stWeek) ){ |
| @@ -1908,16 +1973,17 @@ | |
| 1908 | Blob header = empty_blob; /* Page header text */ |
| 1909 | int nMaxEvents = 1; /* for calculating length of graph |
| 1910 | bars. */ |
| 1911 | int iterations = 0; /* number of weeks/months we iterate |
| 1912 | over */ |
| 1913 | blob_appendf(&header, "Timeline Events by year%s", |
| 1914 | (includeMonth ? "/month" : "")); |
| 1915 | blob_appendf(&sql, |
| 1916 | "SELECT substr(date(mtime),1,%d) AS timeframe, " |
| 1917 | "count(*) AS eventCount " |
| 1918 | "FROM event ", |
| 1919 | includeMonth ? 7 : 4); |
| 1920 | if(zUserName&&*zUserName){ |
| 1921 | blob_appendf(&sql, " WHERE user=%Q ", zUserName); |
| 1922 | blob_appendf(&header," for user %q", zUserName); |
| 1923 | } |
| @@ -2053,14 +2119,15 @@ | |
| 2053 | int rowClass = 0; /* counter for alternating |
| 2054 | row colors */ |
| 2055 | Blob sql = empty_blob; /* SQL */ |
| 2056 | int nMaxEvents = 1; /* max number of events for |
| 2057 | all rows. */ |
| 2058 | blob_append(&sql, |
| 2059 | "SELECT user, " |
| 2060 | "COUNT(*) AS eventCount " |
| 2061 | "FROM event " |
| 2062 | "GROUP BY user ORDER BY eventCount DESC", |
| 2063 | -1); |
| 2064 | db_prepare(&query, blob_str(&sql)); |
| 2065 | blob_reset(&sql); |
| 2066 | @ <h1>Timeline Events by User</h1> |
| @@ -2121,14 +2188,15 @@ | |
| 2121 | Blob sql = empty_blob; |
| 2122 | int nMaxEvents = 1; /* max number of events for |
| 2123 | all rows. */ |
| 2124 | int iterations = 0; /* # of active time periods. */ |
| 2125 | |
| 2126 | cgi_printf("Select year: "); |
| 2127 | blob_append(&sql, |
| 2128 | "SELECT DISTINCT substr(date(mtime),1,4) AS y " |
| 2129 | "FROM event WHERE 1 ", -1); |
| 2130 | if(zUserName&&*zUserName){ |
| 2131 | blob_appendf(&sql,"AND user=%Q ", zUserName); |
| 2132 | } |
| 2133 | blob_append(&sql,"GROUP BY y ORDER BY y", -1); |
| 2134 | db_prepare(&qYears, blob_str(&sql)); |
| @@ -2159,11 +2227,11 @@ | |
| 2159 | blob_appendf(&header, "Timeline events for the calendar weeks " |
| 2160 | "of %h", zYear); |
| 2161 | blob_appendf(&sql, |
| 2162 | "SELECT DISTINCT strftime('%%%%W',mtime) AS wk, " |
| 2163 | "count(*) AS n " |
| 2164 | "FROM event " |
| 2165 | "WHERE %Q=substr(date(mtime),1,4) " |
| 2166 | "AND mtime < current_timestamp ", |
| 2167 | zYear); |
| 2168 | if(zUserName&&*zUserName){ |
| 2169 | blob_appendf(&sql, " AND user=%Q ", zUserName); |
| 2170 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1850,10 +1850,75 @@ | |
| 1850 | @ <a href="%s(g.zTop)/timeline?p=%S(zUuid)&d=%S(zUuid)">%S(zUuid)</a> |
| 1851 | } |
| 1852 | db_finalize(&q); |
| 1853 | style_footer(); |
| 1854 | } |
| 1855 | |
| 1856 | /* |
| 1857 | ** Creates a TEMP VIEW named v_reports which is a wrapper around the |
| 1858 | ** EVENT table filtered on event.type. It looks for the request |
| 1859 | ** parameter 'type' (reminder: we "should" use 'y' for consistency |
| 1860 | ** with /timeline, but /reports uses 'y' for the year) and expects it |
| 1861 | ** to contain one of the conventional values from event.type or the |
| 1862 | ** value "all", which is treated as equivalent to "*". By default (if |
| 1863 | ** no 'y' is specified), "*" is assumed (that is also the default for |
| 1864 | ** invalid/unknown filter values). That 'y' filter is the one used for |
| 1865 | ** the event list. Note that a filter of "*" or "all" is equivalent to |
| 1866 | ** querying against the full event table. The view, however, adds an |
| 1867 | ** abstraction level to simplify the implementation code for the |
| 1868 | ** various /reports pages. |
| 1869 | ** |
| 1870 | ** Returns one of: 'c', 'w', 'g', 't', 'e', representing the type of |
| 1871 | ** filter it applies, or '*' if no filter is applied (i.e. if "all" is |
| 1872 | ** used). |
| 1873 | */ |
| 1874 | static char stats_report_init_view(){ |
| 1875 | char const * zType = PD("type","*"); /* analog to /timeline?y=... */ |
| 1876 | char const * zRealType = NULL; /* normalized form of zType */ |
| 1877 | char rc = 0; /* result code */ |
| 1878 | switch( (zType && *zType) ? *zType : 0 ){ |
| 1879 | case 'c': |
| 1880 | case 'C': |
| 1881 | zRealType = "ci"; |
| 1882 | rc = *zRealType; |
| 1883 | break; |
| 1884 | case 'e': |
| 1885 | case 'E': |
| 1886 | zRealType = "e"; |
| 1887 | rc = *zRealType; |
| 1888 | break; |
| 1889 | case 'g': |
| 1890 | case 'G': |
| 1891 | zRealType = "g"; |
| 1892 | rc = *zRealType; |
| 1893 | break; |
| 1894 | case 't': |
| 1895 | case 'T': |
| 1896 | zRealType = "t"; |
| 1897 | rc = *zRealType; |
| 1898 | break; |
| 1899 | case 'w': |
| 1900 | case 'W': |
| 1901 | zRealType = "w"; |
| 1902 | rc = *zRealType; |
| 1903 | break; |
| 1904 | default: |
| 1905 | rc = '*'; |
| 1906 | break; |
| 1907 | } |
| 1908 | assert(0 != rc); |
| 1909 | if(zRealType){ |
| 1910 | db_multi_exec("CREATE TEMP VIEW v_reports AS " |
| 1911 | "SELECT * FROM event WHERE type GLOB %Q", |
| 1912 | zRealType); |
| 1913 | }else{ |
| 1914 | db_multi_exec("CREATE TEMP VIEW v_reports AS " |
| 1915 | "SELECT * FROM event"); |
| 1916 | } |
| 1917 | return rc; |
| 1918 | } |
| 1919 | |
| 1920 | |
| 1921 | /* |
| 1922 | ** Helper for stats_report_by_month_year(), which generates a list of |
| 1923 | ** week numbers. zTimeframe should be either a timeframe in the form YYYY |
| 1924 | ** or YYYY-MM. |
| @@ -1864,11 +1929,11 @@ | |
| 1929 | memcpy(yearPart, zTimeframe, 4); |
| 1930 | db_prepare(&stWeek, |
| 1931 | "SELECT DISTINCT strftime('%%W',mtime) AS wk, " |
| 1932 | "count(*) AS n, " |
| 1933 | "substr(date(mtime),1,%d) AS ym " |
| 1934 | "FROM v_reports " |
| 1935 | "WHERE ym=%Q AND mtime < current_timestamp " |
| 1936 | "GROUP BY wk ORDER BY wk", |
| 1937 | strlen(zTimeframe), |
| 1938 | zTimeframe); |
| 1939 | while( SQLITE_ROW == db_step(&stWeek) ){ |
| @@ -1908,16 +1973,17 @@ | |
| 1973 | Blob header = empty_blob; /* Page header text */ |
| 1974 | int nMaxEvents = 1; /* for calculating length of graph |
| 1975 | bars. */ |
| 1976 | int iterations = 0; /* number of weeks/months we iterate |
| 1977 | over */ |
| 1978 | stats_report_init_view(); |
| 1979 | blob_appendf(&header, "Timeline Events by year%s", |
| 1980 | (includeMonth ? "/month" : "")); |
| 1981 | blob_appendf(&sql, |
| 1982 | "SELECT substr(date(mtime),1,%d) AS timeframe, " |
| 1983 | "count(*) AS eventCount " |
| 1984 | "FROM v_reports ", |
| 1985 | includeMonth ? 7 : 4); |
| 1986 | if(zUserName&&*zUserName){ |
| 1987 | blob_appendf(&sql, " WHERE user=%Q ", zUserName); |
| 1988 | blob_appendf(&header," for user %q", zUserName); |
| 1989 | } |
| @@ -2053,14 +2119,15 @@ | |
| 2119 | int rowClass = 0; /* counter for alternating |
| 2120 | row colors */ |
| 2121 | Blob sql = empty_blob; /* SQL */ |
| 2122 | int nMaxEvents = 1; /* max number of events for |
| 2123 | all rows. */ |
| 2124 | stats_report_init_view(); |
| 2125 | blob_append(&sql, |
| 2126 | "SELECT user, " |
| 2127 | "COUNT(*) AS eventCount " |
| 2128 | "FROM v_reports " |
| 2129 | "GROUP BY user ORDER BY eventCount DESC", |
| 2130 | -1); |
| 2131 | db_prepare(&query, blob_str(&sql)); |
| 2132 | blob_reset(&sql); |
| 2133 | @ <h1>Timeline Events by User</h1> |
| @@ -2121,14 +2188,15 @@ | |
| 2188 | Blob sql = empty_blob; |
| 2189 | int nMaxEvents = 1; /* max number of events for |
| 2190 | all rows. */ |
| 2191 | int iterations = 0; /* # of active time periods. */ |
| 2192 | |
| 2193 | stats_report_init_view(); |
| 2194 | cgi_printf("Select year: "); |
| 2195 | blob_append(&sql, |
| 2196 | "SELECT DISTINCT substr(date(mtime),1,4) AS y " |
| 2197 | "FROM v_reports WHERE 1 ", -1); |
| 2198 | if(zUserName&&*zUserName){ |
| 2199 | blob_appendf(&sql,"AND user=%Q ", zUserName); |
| 2200 | } |
| 2201 | blob_append(&sql,"GROUP BY y ORDER BY y", -1); |
| 2202 | db_prepare(&qYears, blob_str(&sql)); |
| @@ -2159,11 +2227,11 @@ | |
| 2227 | blob_appendf(&header, "Timeline events for the calendar weeks " |
| 2228 | "of %h", zYear); |
| 2229 | blob_appendf(&sql, |
| 2230 | "SELECT DISTINCT strftime('%%%%W',mtime) AS wk, " |
| 2231 | "count(*) AS n " |
| 2232 | "FROM v_reports " |
| 2233 | "WHERE %Q=substr(date(mtime),1,4) " |
| 2234 | "AND mtime < current_timestamp ", |
| 2235 | zYear); |
| 2236 | if(zUserName&&*zUserName){ |
| 2237 | blob_appendf(&sql, " AND user=%Q ", zUserName); |
| 2238 |