Added some basic route statistics to the Control Panel.

Fixed the direction of the labels on the views graph.
This commit is contained in:
Azareal 2018-01-04 10:23:33 +00:00
parent dcfcd08248
commit e259a575e9
8 changed files with 178 additions and 81 deletions

View File

@ -97,6 +97,7 @@ var PreRenderHooks = map[string][]func(http.ResponseWriter, *http.Request, *User
"pre_render_panel_delete_forum": nil, "pre_render_panel_delete_forum": nil,
"pre_render_panel_edit_forum": nil, "pre_render_panel_edit_forum": nil,
"pre_render_panel_analytics": nil, "pre_render_panel_analytics": nil,
"pre_render_panel_analytics_routes": nil,
"pre_render_panel_settings": nil, "pre_render_panel_settings": nil,
"pre_render_panel_setting": nil, "pre_render_panel_setting": nil,
"pre_render_panel_word_filters": nil, "pre_render_panel_word_filters": nil,

View File

@ -163,6 +163,20 @@ type PanelAnalyticsPage struct {
PrimaryGraph PanelTimeGraph PrimaryGraph PanelTimeGraph
} }
type PanelAnalyticsRoutesItem struct {
Route string
Count int
}
type PanelAnalyticsRoutesPage struct {
Title string
CurrentUser User
Header *HeaderVars
Stats PanelStats
Zone string
ItemList []PanelAnalyticsRoutesItem
}
type PanelThemesPage struct { type PanelThemesPage struct {
Title string Title string
CurrentUser User CurrentUser User

View File

@ -50,6 +50,7 @@ var RouteMap = map[string]interface{}{
"routePanelUsersEdit": routePanelUsersEdit, "routePanelUsersEdit": routePanelUsersEdit,
"routePanelUsersEditSubmit": routePanelUsersEditSubmit, "routePanelUsersEditSubmit": routePanelUsersEditSubmit,
"routePanelAnalyticsViews": routePanelAnalyticsViews, "routePanelAnalyticsViews": routePanelAnalyticsViews,
"routePanelAnalyticsRoutes": routePanelAnalyticsRoutes,
"routePanelGroups": routePanelGroups, "routePanelGroups": routePanelGroups,
"routePanelGroupsEdit": routePanelGroupsEdit, "routePanelGroupsEdit": routePanelGroupsEdit,
"routePanelGroupsEditPerms": routePanelGroupsEditPerms, "routePanelGroupsEditPerms": routePanelGroupsEditPerms,
@ -114,31 +115,32 @@ var routeMapEnum = map[string]int{
"routePanelUsersEdit": 32, "routePanelUsersEdit": 32,
"routePanelUsersEditSubmit": 33, "routePanelUsersEditSubmit": 33,
"routePanelAnalyticsViews": 34, "routePanelAnalyticsViews": 34,
"routePanelGroups": 35, "routePanelAnalyticsRoutes": 35,
"routePanelGroupsEdit": 36, "routePanelGroups": 36,
"routePanelGroupsEditPerms": 37, "routePanelGroupsEdit": 37,
"routePanelGroupsEditSubmit": 38, "routePanelGroupsEditPerms": 38,
"routePanelGroupsEditPermsSubmit": 39, "routePanelGroupsEditSubmit": 39,
"routePanelGroupsCreateSubmit": 40, "routePanelGroupsEditPermsSubmit": 40,
"routePanelBackups": 41, "routePanelGroupsCreateSubmit": 41,
"routePanelLogsMod": 42, "routePanelBackups": 42,
"routePanelDebug": 43, "routePanelLogsMod": 43,
"routePanel": 44, "routePanelDebug": 44,
"routeAccountEditCritical": 45, "routePanel": 45,
"routeAccountEditCriticalSubmit": 46, "routeAccountEditCritical": 46,
"routeAccountEditAvatar": 47, "routeAccountEditCriticalSubmit": 47,
"routeAccountEditAvatarSubmit": 48, "routeAccountEditAvatar": 48,
"routeAccountEditUsername": 49, "routeAccountEditAvatarSubmit": 49,
"routeAccountEditUsernameSubmit": 50, "routeAccountEditUsername": 50,
"routeAccountEditEmail": 51, "routeAccountEditUsernameSubmit": 51,
"routeAccountEditEmailTokenSubmit": 52, "routeAccountEditEmail": 52,
"routeProfile": 53, "routeAccountEditEmailTokenSubmit": 53,
"routeBanSubmit": 54, "routeProfile": 54,
"routeUnban": 55, "routeBanSubmit": 55,
"routeActivate": 56, "routeUnban": 56,
"routeIps": 57, "routeActivate": 57,
"routeDynamic": 58, "routeIps": 58,
"routeUploads": 59, "routeDynamic": 59,
"routeUploads": 60,
} }
var reverseRouteMapEnum = map[int]string{ var reverseRouteMapEnum = map[int]string{
0: "routeAPI", 0: "routeAPI",
@ -176,31 +178,32 @@ var reverseRouteMapEnum = map[int]string{
32: "routePanelUsersEdit", 32: "routePanelUsersEdit",
33: "routePanelUsersEditSubmit", 33: "routePanelUsersEditSubmit",
34: "routePanelAnalyticsViews", 34: "routePanelAnalyticsViews",
35: "routePanelGroups", 35: "routePanelAnalyticsRoutes",
36: "routePanelGroupsEdit", 36: "routePanelGroups",
37: "routePanelGroupsEditPerms", 37: "routePanelGroupsEdit",
38: "routePanelGroupsEditSubmit", 38: "routePanelGroupsEditPerms",
39: "routePanelGroupsEditPermsSubmit", 39: "routePanelGroupsEditSubmit",
40: "routePanelGroupsCreateSubmit", 40: "routePanelGroupsEditPermsSubmit",
41: "routePanelBackups", 41: "routePanelGroupsCreateSubmit",
42: "routePanelLogsMod", 42: "routePanelBackups",
43: "routePanelDebug", 43: "routePanelLogsMod",
44: "routePanel", 44: "routePanelDebug",
45: "routeAccountEditCritical", 45: "routePanel",
46: "routeAccountEditCriticalSubmit", 46: "routeAccountEditCritical",
47: "routeAccountEditAvatar", 47: "routeAccountEditCriticalSubmit",
48: "routeAccountEditAvatarSubmit", 48: "routeAccountEditAvatar",
49: "routeAccountEditUsername", 49: "routeAccountEditAvatarSubmit",
50: "routeAccountEditUsernameSubmit", 50: "routeAccountEditUsername",
51: "routeAccountEditEmail", 51: "routeAccountEditUsernameSubmit",
52: "routeAccountEditEmailTokenSubmit", 52: "routeAccountEditEmail",
53: "routeProfile", 53: "routeAccountEditEmailTokenSubmit",
54: "routeBanSubmit", 54: "routeProfile",
55: "routeUnban", 55: "routeBanSubmit",
56: "routeActivate", 56: "routeUnban",
57: "routeIps", 57: "routeActivate",
58: "routeDynamic", 58: "routeIps",
59: "routeUploads", 59: "routeDynamic",
60: "routeUploads",
} }
// TODO: Stop spilling these into the package scope? // TODO: Stop spilling these into the package scope?
@ -571,14 +574,17 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
case "/panel/analytics/views/": case "/panel/analytics/views/":
common.RouteViewCounter.Bump(34) common.RouteViewCounter.Bump(34)
err = routePanelAnalyticsViews(w,req,user) err = routePanelAnalyticsViews(w,req,user)
case "/panel/groups/": case "/panel/analytics/routes/":
common.RouteViewCounter.Bump(35) common.RouteViewCounter.Bump(35)
err = routePanelAnalyticsRoutes(w,req,user)
case "/panel/groups/":
common.RouteViewCounter.Bump(36)
err = routePanelGroups(w,req,user) err = routePanelGroups(w,req,user)
case "/panel/groups/edit/": case "/panel/groups/edit/":
common.RouteViewCounter.Bump(36) common.RouteViewCounter.Bump(37)
err = routePanelGroupsEdit(w,req,user,extraData) err = routePanelGroupsEdit(w,req,user,extraData)
case "/panel/groups/edit/perms/": case "/panel/groups/edit/perms/":
common.RouteViewCounter.Bump(37) common.RouteViewCounter.Bump(38)
err = routePanelGroupsEditPerms(w,req,user,extraData) err = routePanelGroupsEditPerms(w,req,user,extraData)
case "/panel/groups/edit/submit/": case "/panel/groups/edit/submit/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -587,7 +593,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(38) common.RouteViewCounter.Bump(39)
err = routePanelGroupsEditSubmit(w,req,user,extraData) err = routePanelGroupsEditSubmit(w,req,user,extraData)
case "/panel/groups/edit/perms/submit/": case "/panel/groups/edit/perms/submit/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -596,7 +602,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(39) common.RouteViewCounter.Bump(40)
err = routePanelGroupsEditPermsSubmit(w,req,user,extraData) err = routePanelGroupsEditPermsSubmit(w,req,user,extraData)
case "/panel/groups/create/": case "/panel/groups/create/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -605,7 +611,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(40) common.RouteViewCounter.Bump(41)
err = routePanelGroupsCreateSubmit(w,req,user) err = routePanelGroupsCreateSubmit(w,req,user)
case "/panel/backups/": case "/panel/backups/":
err = common.SuperAdminOnly(w,req,user) err = common.SuperAdminOnly(w,req,user)
@ -614,10 +620,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(41) common.RouteViewCounter.Bump(42)
err = routePanelBackups(w,req,user,extraData) err = routePanelBackups(w,req,user,extraData)
case "/panel/logs/mod/": case "/panel/logs/mod/":
common.RouteViewCounter.Bump(42) common.RouteViewCounter.Bump(43)
err = routePanelLogsMod(w,req,user) err = routePanelLogsMod(w,req,user)
case "/panel/debug/": case "/panel/debug/":
err = common.AdminOnly(w,req,user) err = common.AdminOnly(w,req,user)
@ -626,10 +632,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(43) common.RouteViewCounter.Bump(44)
err = routePanelDebug(w,req,user) err = routePanelDebug(w,req,user)
default: default:
common.RouteViewCounter.Bump(44) common.RouteViewCounter.Bump(45)
err = routePanel(w,req,user) err = routePanel(w,req,user)
} }
if err != nil { if err != nil {
@ -644,7 +650,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(45) common.RouteViewCounter.Bump(46)
err = routeAccountEditCritical(w,req,user) err = routeAccountEditCritical(w,req,user)
case "/user/edit/critical/submit/": case "/user/edit/critical/submit/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -659,7 +665,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(46) common.RouteViewCounter.Bump(47)
err = routeAccountEditCriticalSubmit(w,req,user) err = routeAccountEditCriticalSubmit(w,req,user)
case "/user/edit/avatar/": case "/user/edit/avatar/":
err = common.MemberOnly(w,req,user) err = common.MemberOnly(w,req,user)
@ -668,7 +674,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(47) common.RouteViewCounter.Bump(48)
err = routeAccountEditAvatar(w,req,user) err = routeAccountEditAvatar(w,req,user)
case "/user/edit/avatar/submit/": case "/user/edit/avatar/submit/":
err = common.MemberOnly(w,req,user) err = common.MemberOnly(w,req,user)
@ -677,7 +683,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(48) common.RouteViewCounter.Bump(49)
err = routeAccountEditAvatarSubmit(w,req,user) err = routeAccountEditAvatarSubmit(w,req,user)
case "/user/edit/username/": case "/user/edit/username/":
err = common.MemberOnly(w,req,user) err = common.MemberOnly(w,req,user)
@ -686,7 +692,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(49) common.RouteViewCounter.Bump(50)
err = routeAccountEditUsername(w,req,user) err = routeAccountEditUsername(w,req,user)
case "/user/edit/username/submit/": case "/user/edit/username/submit/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -701,7 +707,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(50) common.RouteViewCounter.Bump(51)
err = routeAccountEditUsernameSubmit(w,req,user) err = routeAccountEditUsernameSubmit(w,req,user)
case "/user/edit/email/": case "/user/edit/email/":
err = common.MemberOnly(w,req,user) err = common.MemberOnly(w,req,user)
@ -710,7 +716,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(51) common.RouteViewCounter.Bump(52)
err = routeAccountEditEmail(w,req,user) err = routeAccountEditEmail(w,req,user)
case "/user/edit/token/": case "/user/edit/token/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -725,11 +731,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(52) common.RouteViewCounter.Bump(53)
err = routeAccountEditEmailTokenSubmit(w,req,user,extraData) err = routeAccountEditEmailTokenSubmit(w,req,user,extraData)
default: default:
req.URL.Path += extraData req.URL.Path += extraData
common.RouteViewCounter.Bump(53) common.RouteViewCounter.Bump(54)
err = routeProfile(w,req,user) err = routeProfile(w,req,user)
} }
if err != nil { if err != nil {
@ -750,7 +756,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(54) common.RouteViewCounter.Bump(55)
err = routeBanSubmit(w,req,user,extraData) err = routeBanSubmit(w,req,user,extraData)
case "/users/unban/": case "/users/unban/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -765,7 +771,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(55) common.RouteViewCounter.Bump(56)
err = routeUnban(w,req,user,extraData) err = routeUnban(w,req,user,extraData)
case "/users/activate/": case "/users/activate/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -780,7 +786,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(56) common.RouteViewCounter.Bump(57)
err = routeActivate(w,req,user,extraData) err = routeActivate(w,req,user,extraData)
case "/users/ips/": case "/users/ips/":
err = common.MemberOnly(w,req,user) err = common.MemberOnly(w,req,user)
@ -789,7 +795,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(57) common.RouteViewCounter.Bump(58)
err = routeIps(w,req,user) err = routeIps(w,req,user)
} }
if err != nil { if err != nil {
@ -806,7 +812,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
common.NotFound(w,req) common.NotFound(w,req)
return return
} }
common.RouteViewCounter.Bump(59) common.RouteViewCounter.Bump(60)
req.URL.Path += extraData req.URL.Path += extraData
// TODO: Find a way to propagate errors up from this? // TODO: Find a way to propagate errors up from this?
router.UploadHandler(w,req) // TODO: Count these views router.UploadHandler(w,req) // TODO: Count these views
@ -850,7 +856,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
router.RUnlock() router.RUnlock()
if ok { if ok {
common.RouteViewCounter.Bump(58) // TODO: Be more specific about *which* dynamic route it is common.RouteViewCounter.Bump(59) // TODO: Be more specific about *which* dynamic route it is
req.URL.Path += extraData req.URL.Path += extraData
err = handle(w,req,user) err = handle(w,req,user)
if err != nil { if err != nil {

View File

@ -450,26 +450,24 @@ func routePanelAnalyticsViews(w http.ResponseWriter, r *http.Request, user commo
} }
var viewList []int64 var viewList []int64
log.Print("in routePanelAnalyticsViews")
acc := qgen.Builder.Accumulator() acc := qgen.Builder.Accumulator()
rows, err := acc.Select("viewchunks").Columns("count, createdAt, route").Where("route = ''").DateCutoff("createdAt", 6, "hour").Query() rows, err := acc.Select("viewchunks").Columns("count, createdAt").Where("route = ''").DateCutoff("createdAt", 6, "hour").Query()
if err != nil && err != ErrNoRows { if err != nil && err != ErrNoRows {
return common.InternalError(err, w, r) return common.InternalError(err, w, r)
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
log.Print("WE HAVE ROWS")
var count int64 var count int64
var createdAt time.Time var createdAt time.Time
var route string err := rows.Scan(&count, &createdAt)
err := rows.Scan(&count, &createdAt, &route)
if err != nil { if err != nil {
return common.InternalError(err, w, r) return common.InternalError(err, w, r)
} }
log.Print("count: ", count) log.Print("count: ", count)
log.Print("createdAt: ", createdAt) log.Print("createdAt: ", createdAt)
log.Print("route: ", route)
var unixCreatedAt = createdAt.Unix() var unixCreatedAt = createdAt.Unix()
log.Print("unixCreatedAt: ", unixCreatedAt) log.Print("unixCreatedAt: ", unixCreatedAt)
@ -504,6 +502,61 @@ func routePanelAnalyticsViews(w http.ResponseWriter, r *http.Request, user commo
return nil return nil
} }
func routePanelAnalyticsRoutes(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
var routeMap = make(map[string]int)
acc := qgen.Builder.Accumulator()
rows, err := acc.Select("viewchunks").Columns("count, createdAt, route").Where("route != ''").DateCutoff("createdAt", 1, "day").Query()
if err != nil && err != ErrNoRows {
return common.InternalError(err, w, r)
}
defer rows.Close()
for rows.Next() {
var count int
var createdAt time.Time
var route string
err := rows.Scan(&count, &createdAt, &route)
if err != nil {
return common.InternalError(err, w, r)
}
log.Print("count: ", count)
log.Print("createdAt: ", createdAt)
log.Print("route: ", route)
routeMap[route] += count
}
err = rows.Err()
if err != nil {
return common.InternalError(err, w, r)
}
// TODO: Sort this slice
var routeItems []common.PanelAnalyticsRoutesItem
for route, count := range routeMap {
routeItems = append(routeItems, common.PanelAnalyticsRoutesItem{
Route: route,
Count: count,
})
}
pi := common.PanelAnalyticsRoutesPage{common.GetTitlePhrase("panel-analytics"), user, headerVars, stats, "analytics", routeItems}
if common.PreRenderHooks["pre_render_panel_analytics_routes"] != nil {
if common.RunPreRenderHook("pre_render_panel_analytics_routes", w, r, &user, &pi) {
return nil
}
}
err = common.Templates.ExecuteTemplate(w, "panel-analytics-routes.html", pi)
if err != nil {
return common.InternalError(err, w, r)
}
return nil
}
func routePanelSettings(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { func routePanelSettings(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user) headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
if ferr != nil { if ferr != nil {

View File

@ -90,6 +90,7 @@ func buildPanelRoutes() {
Action("routePanelUsersEditSubmit", "/panel/users/edit/submit/", "extraData"), Action("routePanelUsersEditSubmit", "/panel/users/edit/submit/", "extraData"),
View("routePanelAnalyticsViews", "/panel/analytics/views/"), View("routePanelAnalyticsViews", "/panel/analytics/views/"),
View("routePanelAnalyticsRoutes", "/panel/analytics/routes/"),
View("routePanelGroups", "/panel/groups/"), View("routePanelGroups", "/panel/groups/"),
View("routePanelGroupsEdit", "/panel/groups/edit/", "extraData"), View("routePanelGroupsEdit", "/panel/groups/edit/", "extraData"),

View File

@ -0,0 +1,18 @@
{{template "header.html" . }}
<div class="colstack panel_stack">
{{template "panel-menu.html" . }}
<main id="panel_dashboard_right" class="colstack_right">
<div class="colstack_item colstack_head">
<div class="rowitem"><a>Routes</a></div>
</div>
<div id="panel_analytics_routes" class="colstack_item rowlist">
{{range .ItemList}}
<div class="rowitem panel_compactrow editable_parent">
<a href="/panel/groups/edit/{{.Route}}" class="panel_upshift">{{.Route}}</a>
<span class="panel_compacttext to_right">{{.Count}}</span>
</div>
{{end}}
</div>
</main>
</div>
{{template "footer.html" . }}

View File

@ -5,7 +5,7 @@
<div class="colstack_item colstack_head"> <div class="colstack_item colstack_head">
<div class="rowitem"><a>Views</a></div> <div class="rowitem"><a>Views</a></div>
</div> </div>
<div id="panel_analytics" class="colstack_graph_holder"> <div id="panel_analytics_views" class="colstack_graph_holder">
<div class="ct-chart"></div> <div class="ct-chart"></div>
</div> </div>
</main> </main>
@ -23,6 +23,7 @@ for(const i in rawLabels) {
console.log("label:", label); console.log("label:", label);
labels.push(label); labels.push(label);
} }
labels = labels.reverse()
let seriesData = [{{range .PrimaryGraph.Series}} let seriesData = [{{range .PrimaryGraph.Series}}
{{.}},{{end}} {{.}},{{end}}

View File

@ -32,6 +32,9 @@
<div class="rowitem passive submenu"> <div class="rowitem passive submenu">
<a href="/panel/analytics/posts/">Posts</a> <a href="/panel/analytics/posts/">Posts</a>
</div> </div>
<div class="rowitem passive submenu">
<a href="/panel/analytics/routes/">Routes</a>
</div>
<div class="rowitem passive submenu"> <div class="rowitem passive submenu">
<a href="/panel/analytics/crawlers/">Crawlers</a> <a href="/panel/analytics/crawlers/">Crawlers</a>
</div> </div>