2018-05-27 09:36:35 +00:00
package panel
import (
"database/sql"
"errors"
"log"
"net/http"
"strconv"
2019-05-18 00:33:35 +00:00
"strings"
2018-05-27 09:36:35 +00:00
"time"
2019-04-19 06:36:26 +00:00
c "github.com/Azareal/Gosora/common"
2019-10-11 00:57:33 +00:00
p "github.com/Azareal/Gosora/common/phrases"
2020-02-23 09:08:47 +00:00
qgen "github.com/Azareal/Gosora/query_gen"
2018-05-27 09:36:35 +00:00
)
2021-05-03 01:53:05 +00:00
func analyticsTimeRange ( rawTimeRange string ) ( * c . AnalyticsTimeRange , error ) {
tr := & c . AnalyticsTimeRange {
2020-02-23 09:08:47 +00:00
Quantity : 6 ,
Unit : "hour" ,
Slices : 12 ,
2019-09-29 05:10:05 +00:00
SliceWidth : 60 * 30 ,
2020-02-23 09:08:47 +00:00
Range : "six-hours" ,
2019-09-29 05:10:05 +00:00
}
2018-05-27 09:36:35 +00:00
switch rawTimeRange {
2019-02-23 06:29:19 +00:00
// This might be pushing it, we might want to come up with a more efficient scheme for dealing with large timeframes like this
2019-02-24 01:29:06 +00:00
case "one-year" :
2021-05-03 01:53:05 +00:00
tr . Quantity = 12
tr . Unit = "month"
tr . Slices = 12
tr . SliceWidth = 60 * 60 * 24 * 30
2019-02-23 06:29:19 +00:00
case "three-months" :
2021-05-03 01:53:05 +00:00
tr . Quantity = 90
tr . Unit = "day"
tr . Slices = 30
tr . SliceWidth = 60 * 60 * 24 * 3
2018-05-27 09:36:35 +00:00
case "one-month" :
2021-05-03 01:53:05 +00:00
tr . Quantity = 30
tr . Unit = "day"
tr . Slices = 30
tr . SliceWidth = 60 * 60 * 24
2018-05-27 09:36:35 +00:00
case "one-week" :
2021-05-03 01:53:05 +00:00
tr . Quantity = 7
tr . Unit = "day"
tr . Slices = 14
tr . SliceWidth = 60 * 60 * 12
2018-05-27 09:36:35 +00:00
case "two-days" : // Two days is experimental
2021-05-03 01:53:05 +00:00
tr . Quantity = 2
tr . Unit = "day"
tr . Slices = 24
tr . SliceWidth = 60 * 60 * 2
2018-05-27 09:36:35 +00:00
case "one-day" :
2021-05-03 01:53:05 +00:00
tr . Quantity = 1
tr . Unit = "day"
tr . Slices = 24
tr . SliceWidth = 60 * 60
2018-05-27 09:36:35 +00:00
case "twelve-hours" :
2021-05-03 01:53:05 +00:00
tr . Quantity = 12
tr . Slices = 24
2018-05-27 09:36:35 +00:00
case "six-hours" , "" :
2021-05-03 01:53:05 +00:00
return tr , nil
2018-05-27 09:36:35 +00:00
default :
2021-05-03 01:53:05 +00:00
return tr , errors . New ( "Unknown time range" )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
tr . Range = rawTimeRange
return tr , nil
2018-05-27 09:36:35 +00:00
}
2019-05-01 23:14:07 +00:00
type pAvg struct {
Avg int64
Tot int64
}
func analyticsRowsToAverageMap ( rows * sql . Rows , labelList [ ] int64 , avgMap map [ int64 ] int64 ) ( map [ int64 ] int64 , error ) {
defer rows . Close ( )
for rows . Next ( ) {
var count int64
var createdAt time . Time
2021-05-03 01:53:05 +00:00
e := rows . Scan ( & count , & createdAt )
if e != nil {
return avgMap , e
2019-05-01 23:14:07 +00:00
}
2019-09-29 05:10:05 +00:00
unixCreatedAt := createdAt . Unix ( )
2019-05-01 23:14:07 +00:00
// TODO: Bulk log this
if c . Dev . SuperDebug {
log . Print ( "count: " , count )
2021-05-03 01:53:05 +00:00
log . Print ( "createdAt: " , createdAt , " - " , unixCreatedAt )
2019-05-01 23:14:07 +00:00
}
2019-09-29 05:10:05 +00:00
pAvgMap := make ( map [ int64 ] pAvg )
2019-05-01 23:14:07 +00:00
for _ , value := range labelList {
if unixCreatedAt > value {
prev := pAvgMap [ value ]
prev . Avg += count
prev . Tot ++
pAvgMap [ value ] = prev
break
}
}
for key , pAvg := range pAvgMap {
avgMap [ key ] = pAvg . Avg / pAvg . Tot
}
}
return avgMap , rows . Err ( )
}
2019-05-19 01:01:11 +00:00
func analyticsRowsToAverageMap2 ( rows * sql . Rows , labelList [ ] int64 , avgMap map [ int64 ] int64 , typ int ) ( map [ int64 ] int64 , error ) {
2019-05-09 06:58:55 +00:00
defer rows . Close ( )
for rows . Next ( ) {
var stack , heap int64
var createdAt time . Time
2021-05-03 01:53:05 +00:00
e := rows . Scan ( & stack , & heap , & createdAt )
if e != nil {
return avgMap , e
2019-05-09 06:58:55 +00:00
}
2019-09-29 05:10:05 +00:00
unixCreatedAt := createdAt . Unix ( )
2019-05-09 06:58:55 +00:00
// TODO: Bulk log this
if c . Dev . SuperDebug {
log . Print ( "stack: " , stack )
log . Print ( "heap: " , heap )
2021-05-03 01:53:05 +00:00
log . Print ( "createdAt: " , createdAt , " - " , unixCreatedAt )
2019-05-09 06:58:55 +00:00
}
2019-05-19 01:01:11 +00:00
if typ == 1 {
heap = 0
} else if typ == 2 {
stack = 0
}
2019-09-29 05:10:05 +00:00
pAvgMap := make ( map [ int64 ] pAvg )
2019-05-09 06:58:55 +00:00
for _ , value := range labelList {
if unixCreatedAt > value {
prev := pAvgMap [ value ]
prev . Avg += stack + heap
prev . Tot ++
pAvgMap [ value ] = prev
break
}
}
for key , pAvg := range pAvgMap {
avgMap [ key ] = pAvg . Avg / pAvg . Tot
}
}
return avgMap , rows . Err ( )
}
2020-02-23 09:08:47 +00:00
func analyticsRowsToAverageMap3 ( rows * sql . Rows , labelList [ ] int64 , avgMap map [ int64 ] int64 , typ int ) ( map [ int64 ] int64 , error ) {
defer rows . Close ( )
for rows . Next ( ) {
var low , high , avg int64
var createdAt time . Time
2021-05-03 01:53:05 +00:00
e := rows . Scan ( & low , & high , & avg , & createdAt )
if e != nil {
return avgMap , e
2020-02-23 09:08:47 +00:00
}
unixCreatedAt := createdAt . Unix ( )
// TODO: Bulk log this
if c . Dev . SuperDebug {
log . Print ( "low: " , low )
log . Print ( "high: " , high )
log . Print ( "avg: " , avg )
2021-05-03 01:53:05 +00:00
log . Print ( "createdAt: " , createdAt , " - " , unixCreatedAt )
2020-02-23 09:08:47 +00:00
}
var dat int64
switch typ {
case 0 :
dat = low
case 1 :
dat = high
default :
dat = avg
}
pAvgMap := make ( map [ int64 ] pAvg )
2021-05-03 01:53:05 +00:00
for _ , val := range labelList {
if unixCreatedAt > val {
prev := pAvgMap [ val ]
2020-02-23 09:08:47 +00:00
prev . Avg += dat
prev . Tot ++
2021-05-03 01:53:05 +00:00
pAvgMap [ val ] = prev
2020-02-23 09:08:47 +00:00
break
}
}
for key , pAvg := range pAvgMap {
avgMap [ key ] = pAvg . Avg / pAvg . Tot
}
}
return avgMap , rows . Err ( )
}
2021-05-03 01:53:05 +00:00
func PreAnalyticsDetail ( w http . ResponseWriter , r * http . Request , u * c . User ) ( * c . BasePanelPage , c . RouteError ) {
bp , fe := buildBasePage ( w , r , u , "analytics" , "analytics" )
if fe != nil {
return nil , fe
2018-06-06 00:21:22 +00:00
}
2019-10-11 00:57:33 +00:00
bp . AddSheet ( "chartist/chartist.min.css" )
bp . AddScript ( "chartist/chartist.min.js" )
bp . AddScriptAsync ( "analytics.js" )
2019-11-04 07:46:34 +00:00
bp . LooseCSP = true
2019-10-11 00:57:33 +00:00
return bp , nil
2018-06-06 00:21:22 +00:00
}
2021-05-03 01:53:05 +00:00
func createTimeGraph ( series [ ] [ ] int64 , labelList [ ] int64 , legends ... [ ] string ) c . PanelTimeGraph {
var llegends [ ] string
if len ( legends ) > 0 {
llegends = legends [ 0 ]
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
graph := c . PanelTimeGraph { Series : series , Labels : labelList , Legends : llegends }
c . DebugLogf ( "graph: %+v\n" , graph )
return graph
}
func CreateViewListItems ( revLabelList [ ] int64 , viewMap map [ int64 ] int64 ) ( [ ] int64 , [ ] c . PanelAnalyticsItem ) {
viewList := make ( [ ] int64 , len ( revLabelList ) )
viewItems := make ( [ ] c . PanelAnalyticsItem , len ( revLabelList ) )
for i , val := range revLabelList {
viewList [ i ] = viewMap [ val ]
viewItems [ i ] = c . PanelAnalyticsItem { Time : val , Count : viewMap [ val ] }
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
return viewList , viewItems
}
2018-05-27 09:36:35 +00:00
2021-05-03 01:53:05 +00:00
func AnalyticsViews ( w http . ResponseWriter , r * http . Request , u * c . User ) c . RouteError {
bp , fe := PreAnalyticsDetail ( w , r , u )
if fe != nil {
return fe
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , viewMap := c . AnalyticsTimeRangeToLabelList ( tr )
2018-05-27 09:36:35 +00:00
2021-05-03 01:53:05 +00:00
c . DebugLog ( "in panel.AnalyticsViews" )
// TODO: Add some sort of analytics store / iterator?
viewMap , e = c . Analytics . FillViewMap ( "viewchunks" , tr , labelList , viewMap , "route" , "" )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
viewList , viewItems := CreateViewListItems ( revLabelList , viewMap )
2020-02-23 09:08:47 +00:00
2021-05-03 01:53:05 +00:00
graph := createTimeGraph ( [ ] [ ] int64 { viewList } , labelList )
2019-02-24 01:29:06 +00:00
var ttime string
2021-05-03 01:53:05 +00:00
if tr . Range == "six-hours" || tr . Range == "twelve-hours" || tr . Range == "one-day" {
2019-02-24 01:29:06 +00:00
ttime = "time"
}
2019-04-29 06:11:26 +00:00
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsStd { graph , viewItems , tr . Range , tr . Unit , ttime }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_views" , pi } )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
func AnalyticsRouteViews ( w http . ResponseWriter , r * http . Request , u * c . User , route string ) c . RouteError {
bp , fe := PreAnalyticsDetail ( w , r , u )
if fe != nil {
return fe
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , viewMap := c . AnalyticsTimeRangeToLabelList ( tr )
2018-05-27 09:36:35 +00:00
2019-04-19 06:36:26 +00:00
c . DebugLog ( "in panel.AnalyticsRouteViews" )
2018-05-27 09:36:35 +00:00
// TODO: Validate the route is valid
2021-05-03 01:53:05 +00:00
viewMap , e = c . Analytics . FillViewMap ( "viewchunks" , tr , labelList , viewMap , "route" , route )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
viewList , viewItems := CreateViewListItems ( revLabelList , viewMap )
graph := createTimeGraph ( [ ] [ ] int64 { viewList } , labelList )
2018-05-27 09:36:35 +00:00
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsRoutePage { bp , c . SanitiseSingleLine ( route ) , graph , viewItems , tr . Range }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_route_views" , pi } )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
func AnalyticsAgentViews ( w http . ResponseWriter , r * http . Request , u * c . User , agent string ) c . RouteError {
bp , ferr := PreAnalyticsDetail ( w , r , u )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , viewMap := c . AnalyticsTimeRangeToLabelList ( tr )
2018-05-27 09:36:35 +00:00
// ? Only allow valid agents? The problem with this is that agents wind up getting renamed and it would take a migration to get them all up to snuff
2019-04-19 06:36:26 +00:00
agent = c . SanitiseSingleLine ( agent )
2018-05-27 09:36:35 +00:00
2019-04-19 06:36:26 +00:00
c . DebugLog ( "in panel.AnalyticsAgentViews" )
2018-05-27 09:36:35 +00:00
// TODO: Verify the agent is valid
2021-05-03 01:53:05 +00:00
viewMap , e = c . Analytics . FillViewMap ( "viewchunks_agents" , tr , labelList , viewMap , "browser" , agent )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
viewList := CreateViewList ( revLabelList , viewMap )
graph := createTimeGraph ( [ ] [ ] int64 { viewList } , labelList )
2018-05-27 09:36:35 +00:00
2019-10-11 00:57:33 +00:00
friendlyAgent , ok := p . GetUserAgentPhrase ( agent )
2018-05-27 09:36:35 +00:00
if ! ok {
friendlyAgent = agent
}
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsAgentPage { bp , agent , friendlyAgent , graph , tr . Range }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_agent_views" , pi } )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
func AnalyticsForumViews ( w http . ResponseWriter , r * http . Request , u * c . User , sfid string ) c . RouteError {
bp , ferr := PreAnalyticsDetail ( w , r , u )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , viewMap := c . AnalyticsTimeRangeToLabelList ( tr )
2018-05-27 09:36:35 +00:00
2021-05-03 01:53:05 +00:00
fid , e := strconv . Atoi ( sfid )
if e != nil {
return c . LocalError ( "Invalid integer" , w , r , u )
2018-05-27 09:36:35 +00:00
}
2019-04-19 06:36:26 +00:00
c . DebugLog ( "in panel.AnalyticsForumViews" )
2018-05-27 09:36:35 +00:00
// TODO: Verify the agent is valid
2021-05-03 01:53:05 +00:00
viewMap , e = c . Analytics . FillViewMap ( "viewchunks_forums" , tr , labelList , viewMap , "forum" , fid )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
viewList := CreateViewList ( revLabelList , viewMap )
graph := createTimeGraph ( [ ] [ ] int64 { viewList } , labelList )
2018-05-27 09:36:35 +00:00
2021-05-03 01:53:05 +00:00
forum , e := c . Forums . Get ( fid )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsAgentPage { bp , sfid , forum . Name , graph , tr . Range }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_forum_views" , pi } )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
func AnalyticsSystemViews ( w http . ResponseWriter , r * http . Request , u * c . User , system string ) c . RouteError {
bp , ferr := PreAnalyticsDetail ( w , r , u )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , viewMap := c . AnalyticsTimeRangeToLabelList ( tr )
2019-04-19 06:36:26 +00:00
system = c . SanitiseSingleLine ( system )
2018-05-27 09:36:35 +00:00
2019-04-19 06:36:26 +00:00
c . DebugLog ( "in panel.AnalyticsSystemViews" )
2018-05-27 09:36:35 +00:00
// TODO: Verify the OS name is valid
2021-05-03 01:53:05 +00:00
viewMap , e = c . Analytics . FillViewMap ( "viewchunks_systems" , tr , labelList , viewMap , "system" , system )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
viewList := CreateViewList ( revLabelList , viewMap )
graph := createTimeGraph ( [ ] [ ] int64 { viewList } , labelList )
2018-05-27 09:36:35 +00:00
2019-10-11 00:57:33 +00:00
friendlySystem , ok := p . GetOSPhrase ( system )
2018-05-27 09:36:35 +00:00
if ! ok {
friendlySystem = system
}
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsAgentPage { bp , system , friendlySystem , graph , tr . Range }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_system_views" , pi } )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
func CreateViewList ( revLabelList [ ] int64 , viewMap map [ int64 ] int64 ) [ ] int64 {
viewList := make ( [ ] int64 , len ( revLabelList ) )
for i , val := range revLabelList {
viewList [ i ] = viewMap [ val ]
}
return viewList
}
func AnalyticsLanguageViews ( w http . ResponseWriter , r * http . Request , u * c . User , lang string ) c . RouteError {
bp , ferr := PreAnalyticsDetail ( w , r , u )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , viewMap := c . AnalyticsTimeRangeToLabelList ( tr )
2019-04-19 06:36:26 +00:00
lang = c . SanitiseSingleLine ( lang )
2018-05-27 09:36:35 +00:00
2019-04-19 06:36:26 +00:00
c . DebugLog ( "in panel.AnalyticsLanguageViews" )
2018-05-27 09:36:35 +00:00
// TODO: Verify the language code is valid
2021-05-03 01:53:05 +00:00
viewMap , e = c . Analytics . FillViewMap ( "viewchunks_langs" , tr , labelList , viewMap , "lang" , lang )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
viewList := CreateViewList ( revLabelList , viewMap )
graph := createTimeGraph ( [ ] [ ] int64 { viewList } , labelList )
2018-05-27 09:36:35 +00:00
2019-10-11 00:57:33 +00:00
friendlyLang , ok := p . GetHumanLangPhrase ( lang )
2018-05-27 09:36:35 +00:00
if ! ok {
friendlyLang = lang
}
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsAgentPage { bp , lang , friendlyLang , graph , tr . Range }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_lang_views" , pi } )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
func AnalyticsReferrerViews ( w http . ResponseWriter , r * http . Request , u * c . User , domain string ) c . RouteError {
bp , ferr := PreAnalyticsDetail ( w , r , u )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , viewMap := c . AnalyticsTimeRangeToLabelList ( tr )
2018-05-27 09:36:35 +00:00
2019-04-19 06:36:26 +00:00
c . DebugLog ( "in panel.AnalyticsReferrerViews" )
2018-05-27 09:36:35 +00:00
// TODO: Verify the agent is valid
2021-05-03 01:53:05 +00:00
viewMap , e = c . Analytics . FillViewMap ( "viewchunks_referrers" , tr , labelList , viewMap , "domain" , domain )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
viewList := CreateViewList ( revLabelList , viewMap )
graph := createTimeGraph ( [ ] [ ] int64 { viewList } , labelList )
2018-05-27 09:36:35 +00:00
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsAgentPage { bp , c . SanitiseSingleLine ( domain ) , "" , graph , tr . Range }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_referrer_views" , pi } )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
func AnalyticsTopics ( w http . ResponseWriter , r * http . Request , u * c . User ) c . RouteError {
bp , ferr := PreAnalyticsDetail ( w , r , u )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , viewMap := c . AnalyticsTimeRangeToLabelList ( tr )
2018-05-27 09:36:35 +00:00
2019-04-19 06:36:26 +00:00
c . DebugLog ( "in panel.AnalyticsTopics" )
2021-05-03 01:53:05 +00:00
viewMap , e = c . Analytics . FillViewMap ( "topicchunks" , tr , labelList , viewMap , "" )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
viewList , viewItems := CreateViewListItems ( revLabelList , viewMap )
graph := createTimeGraph ( [ ] [ ] int64 { viewList } , labelList )
2018-05-27 09:36:35 +00:00
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsStd { graph , viewItems , tr . Range , tr . Unit , "time" }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_topics" , pi } )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
func AnalyticsPosts ( w http . ResponseWriter , r * http . Request , u * c . User ) c . RouteError {
bp , fe := PreAnalyticsDetail ( w , r , u )
if fe != nil {
return fe
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , viewMap := c . AnalyticsTimeRangeToLabelList ( tr )
2018-05-27 09:36:35 +00:00
2019-04-19 06:36:26 +00:00
c . DebugLog ( "in panel.AnalyticsPosts" )
2021-05-03 01:53:05 +00:00
viewMap , e = c . Analytics . FillViewMap ( "postchunks" , tr , labelList , viewMap , "" )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
viewList , viewItems := CreateViewListItems ( revLabelList , viewMap )
graph := createTimeGraph ( [ ] [ ] int64 { viewList } , labelList )
2018-05-27 09:36:35 +00:00
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsStd { graph , viewItems , tr . Range , tr . Unit , "time" }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_posts" , pi } )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
func AnalyticsMemory ( w http . ResponseWriter , r * http . Request , u * c . User ) c . RouteError {
bp , fe := PreAnalyticsDetail ( w , r , u )
if fe != nil {
return fe
2019-05-01 06:59:51 +00:00
}
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2019-05-01 06:59:51 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , avgMap := c . AnalyticsTimeRangeToLabelList ( tr )
2019-05-01 06:59:51 +00:00
c . DebugLog ( "in panel.AnalyticsMemory" )
2021-05-03 01:53:05 +00:00
rows , e := qgen . NewAcc ( ) . Select ( "memchunks" ) . Columns ( "count,createdAt" ) . DateCutoff ( "createdAt" , tr . Quantity , tr . Unit ) . Query ( )
if e != nil && e != sql . ErrNoRows {
return c . InternalError ( e , w , r )
2019-05-01 06:59:51 +00:00
}
2021-05-03 01:53:05 +00:00
avgMap , e = analyticsRowsToAverageMap ( rows , labelList , avgMap )
if e != nil {
return c . InternalError ( e , w , r )
2019-05-01 06:59:51 +00:00
}
// TODO: Adjust for the missing chunks in week and month
2021-05-03 01:53:05 +00:00
avgList := make ( [ ] int64 , len ( revLabelList ) )
avgItems := make ( [ ] c . PanelAnalyticsItemUnit , len ( revLabelList ) )
for i , value := range revLabelList {
avgList [ i ] = avgMap [ value ]
2019-05-01 23:14:07 +00:00
cv , cu := c . ConvertByteUnit ( float64 ( avgMap [ value ] ) )
2021-05-03 01:53:05 +00:00
avgItems [ i ] = c . PanelAnalyticsItemUnit { Time : value , Unit : cu , Count : int64 ( cv ) }
2019-05-01 06:59:51 +00:00
}
2021-05-03 01:53:05 +00:00
graph := createTimeGraph ( [ ] [ ] int64 { avgList } , labelList )
pi := c . PanelAnalyticsStdUnit { graph , avgItems , tr . Range , tr . Unit , "time" }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_memory" , pi } )
2019-05-01 06:59:51 +00:00
}
2019-05-09 06:58:55 +00:00
// TODO: Show stack and heap memory separately on the chart
2021-05-03 01:53:05 +00:00
func AnalyticsActiveMemory ( w http . ResponseWriter , r * http . Request , u * c . User ) c . RouteError {
bp , ferr := PreAnalyticsDetail ( w , r , u )
2019-05-09 06:58:55 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2019-05-09 06:58:55 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , avgMap := c . AnalyticsTimeRangeToLabelList ( tr )
2019-05-09 06:58:55 +00:00
c . DebugLog ( "in panel.AnalyticsActiveMemory" )
2021-05-03 01:53:05 +00:00
rows , e := qgen . NewAcc ( ) . Select ( "memchunks" ) . Columns ( "stack,heap,createdAt" ) . DateCutoff ( "createdAt" , tr . Quantity , tr . Unit ) . Query ( )
if e != nil && e != sql . ErrNoRows {
return c . InternalError ( e , w , r )
2019-05-09 06:58:55 +00:00
}
2019-05-19 01:01:11 +00:00
var typ int
switch r . FormValue ( "mtype" ) {
case "1" :
typ = 1
case "2" :
typ = 2
default :
typ = 0
}
2021-05-03 01:53:05 +00:00
avgMap , e = analyticsRowsToAverageMap2 ( rows , labelList , avgMap , typ )
if e != nil {
return c . InternalError ( e , w , r )
2019-05-09 06:58:55 +00:00
}
// TODO: Adjust for the missing chunks in week and month
2021-05-03 01:53:05 +00:00
avgList := make ( [ ] int64 , len ( revLabelList ) )
avgItems := make ( [ ] c . PanelAnalyticsItemUnit , len ( revLabelList ) )
for i , value := range revLabelList {
avgList [ i ] = avgMap [ value ]
2019-05-09 06:58:55 +00:00
cv , cu := c . ConvertByteUnit ( float64 ( avgMap [ value ] ) )
2021-05-03 01:53:05 +00:00
avgItems [ i ] = c . PanelAnalyticsItemUnit { Time : value , Unit : cu , Count : int64 ( cv ) }
2019-05-09 06:58:55 +00:00
}
2021-05-03 01:53:05 +00:00
graph := createTimeGraph ( [ ] [ ] int64 { avgList } , labelList )
pi := c . PanelAnalyticsActiveMemory { graph , avgItems , tr . Range , tr . Unit , "time" , typ }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_active_memory" , pi } )
2019-05-09 06:58:55 +00:00
}
2021-05-03 01:53:05 +00:00
func AnalyticsPerf ( w http . ResponseWriter , r * http . Request , u * c . User ) c . RouteError {
bp , ferr := PreAnalyticsDetail ( w , r , u )
2020-02-23 09:08:47 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2020-02-23 09:08:47 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , avgMap := c . AnalyticsTimeRangeToLabelList ( tr )
2020-02-23 09:08:47 +00:00
c . DebugLog ( "in panel.AnalyticsPerf" )
2021-05-03 01:53:05 +00:00
rows , e := qgen . NewAcc ( ) . Select ( "perfchunks" ) . Columns ( "low,high,avg,createdAt" ) . DateCutoff ( "createdAt" , tr . Quantity , tr . Unit ) . Query ( )
if e != nil && e != sql . ErrNoRows {
return c . InternalError ( e , w , r )
2020-02-23 09:08:47 +00:00
}
var typ int
switch r . FormValue ( "type" ) {
case "0" :
typ = 0
case "1" :
typ = 1
default :
typ = 2
}
2021-05-03 01:53:05 +00:00
avgMap , e = analyticsRowsToAverageMap3 ( rows , labelList , avgMap , typ )
if e != nil {
return c . InternalError ( e , w , r )
2020-02-23 09:08:47 +00:00
}
// TODO: Adjust for the missing chunks in week and month
2021-05-03 01:53:05 +00:00
avgList := make ( [ ] int64 , len ( revLabelList ) )
avgItems := make ( [ ] c . PanelAnalyticsItemUnit , len ( revLabelList ) )
for i , value := range revLabelList {
avgList [ i ] = avgMap [ value ]
2020-02-24 08:28:43 +00:00
cv , cu := c . ConvertPerfUnit ( float64 ( avgMap [ value ] ) )
2021-05-03 01:53:05 +00:00
avgItems [ i ] = c . PanelAnalyticsItemUnit { Time : value , Unit : cu , Count : int64 ( cv ) }
2020-02-23 09:08:47 +00:00
}
2021-05-03 01:53:05 +00:00
graph := createTimeGraph ( [ ] [ ] int64 { avgList } , labelList )
pi := c . PanelAnalyticsPerf { graph , avgItems , tr . Range , tr . Unit , "time" , typ }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_performance" , pi } )
2020-02-23 09:08:47 +00:00
}
2020-02-28 04:52:45 +00:00
func analyticsRowsToAvgDuoMap ( rows * sql . Rows , labelList [ ] int64 , avgMap map [ int64 ] int64 ) ( map [ string ] map [ int64 ] int64 , map [ string ] int , error ) {
aMap := make ( map [ string ] map [ int64 ] int64 )
nameMap := make ( map [ string ] int )
defer rows . Close ( )
for rows . Next ( ) {
var count int64
var name string
var createdAt time . Time
2021-05-03 01:53:05 +00:00
e := rows . Scan ( & count , & name , & createdAt )
if e != nil {
return aMap , nameMap , e
2020-02-28 04:52:45 +00:00
}
// TODO: Bulk log this
unixCreatedAt := createdAt . Unix ( )
if c . Dev . SuperDebug {
log . Print ( "count: " , count )
log . Print ( "name: " , name )
2021-05-03 01:53:05 +00:00
log . Print ( "createdAt: " , createdAt , " - " , unixCreatedAt )
2020-02-28 04:52:45 +00:00
}
vvMap , ok := aMap [ name ]
if ! ok {
vvMap = make ( map [ int64 ] int64 )
for key , val := range avgMap {
vvMap [ key ] = val
}
aMap [ name ] = vvMap
}
for _ , value := range labelList {
if unixCreatedAt > value {
vvMap [ value ] = ( vvMap [ value ] + count ) / 2
break
}
}
nameMap [ name ] = ( nameMap [ name ] + int ( count ) ) / 2
}
return aMap , nameMap , rows . Err ( )
}
2021-05-03 01:53:05 +00:00
func sortOVList ( ovList [ ] OVItem ) [ ] OVItem {
2020-02-28 04:52:45 +00:00
// Use bubble sort for now as there shouldn't be too many items
for i := 0 ; i < len ( ovList ) - 1 ; i ++ {
for j := 0 ; j < len ( ovList ) - 1 ; j ++ {
if ovList [ j ] . count > ovList [ j + 1 ] . count {
temp := ovList [ j ]
ovList [ j ] = ovList [ j + 1 ]
ovList [ j + 1 ] = temp
}
}
}
// Invert the direction
2021-05-03 01:53:05 +00:00
tOVList := make ( [ ] OVItem , len ( ovList ) )
for i , ii := len ( ovList ) - 1 , 0 ; i >= 0 ; i -- {
tOVList [ ii ] = ovList [ i ]
ii ++
2020-02-28 04:52:45 +00:00
}
return tOVList
}
2021-05-03 01:53:05 +00:00
func analyticsAMapToOVList ( aMap map [ string ] map [ int64 ] int64 ) [ ] OVItem {
2020-02-28 04:52:45 +00:00
// Order the map
2021-05-03 01:53:05 +00:00
ovList , i := make ( [ ] OVItem , len ( aMap ) ) , 0
2020-02-28 04:52:45 +00:00
for name , avgMap := range aMap {
var totcount int
for _ , count := range avgMap {
totcount = ( totcount + int ( count ) ) / 2
}
2021-05-03 01:53:05 +00:00
ovList [ i ] = OVItem { name , totcount , avgMap }
i ++
2020-02-28 04:52:45 +00:00
}
return sortOVList ( ovList )
}
2021-05-03 01:53:05 +00:00
func AnalyticsRoutesPerf ( w http . ResponseWriter , r * http . Request , u * c . User ) c . RouteError {
bp , ferr := PreAnalyticsDetail ( w , r , u )
2020-02-28 04:52:45 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
bp . AddScript ( "chartist/chartist-plugin-legend.min.js" )
bp . AddSheet ( "chartist/chartist-plugin-legend.css" )
2020-02-28 04:52:45 +00:00
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2020-02-28 04:52:45 +00:00
}
// avgMap contains timestamps but not the averages for those stamps
2021-05-03 01:53:05 +00:00
revLabelList , labelList , avgMap := c . AnalyticsTimeRangeToLabelList ( tr )
2020-02-28 04:52:45 +00:00
2021-05-03 01:53:05 +00:00
rows , e := qgen . NewAcc ( ) . Select ( "viewchunks" ) . Columns ( "avg,route,createdAt" ) . Where ( "count!=0 AND route!=''" ) . DateCutoff ( "createdAt" , tr . Quantity , tr . Unit ) . Query ( )
if e != nil && e != sql . ErrNoRows {
return c . InternalError ( e , w , r )
2020-02-28 04:52:45 +00:00
}
2021-05-03 01:53:05 +00:00
aMap , routeMap , e := analyticsRowsToAvgDuoMap ( rows , labelList , avgMap )
if e != nil {
return c . InternalError ( e , w , r )
2020-02-28 04:52:45 +00:00
}
//c.DebugLogf("aMap: %+v\n", aMap)
//c.DebugLogf("routeMap: %+v\n", routeMap)
ovList := analyticsAMapToOVList ( aMap )
//c.DebugLogf("ovList: %+v\n", ovList)
ex := strings . Split ( r . FormValue ( "ex" ) , "," )
inEx := func ( name string ) bool {
for _ , e := range ex {
if e == name {
return true
}
}
return false
}
var vList [ ] [ ] int64
var legendList [ ] string
var i int
for _ , ovitem := range ovList {
if inEx ( ovitem . name ) {
continue
}
2020-02-29 07:34:38 +00:00
if strings . HasPrefix ( ovitem . name , "panel." ) {
2020-02-28 23:11:07 +00:00
continue
}
2021-05-03 01:53:05 +00:00
viewList := make ( [ ] int64 , len ( revLabelList ) )
for i , val := range revLabelList {
viewList [ i ] = ovitem . viewMap [ val ]
2020-02-28 04:52:45 +00:00
}
vList = append ( vList , viewList )
2020-02-29 07:34:38 +00:00
shortName := strings . Replace ( ovitem . name , "routes." , "r." , - 1 )
legendList = append ( legendList , shortName )
if i >= 7 {
2020-02-28 04:52:45 +00:00
break
}
i ++
}
2021-05-03 01:53:05 +00:00
graph := createTimeGraph ( vList , labelList , legendList )
2020-02-28 04:52:45 +00:00
// TODO: Sort this slice
var routeItems [ ] c . PanelAnalyticsRoutesPerfItem
for route , count := range routeMap {
if inEx ( route ) {
continue
}
cv , cu := c . ConvertPerfUnit ( float64 ( count ) )
routeItems = append ( routeItems , c . PanelAnalyticsRoutesPerfItem {
Route : route ,
2020-02-29 07:34:38 +00:00
Unit : cu ,
2020-02-28 04:52:45 +00:00
Count : int ( cv ) ,
} )
}
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsRoutesPerfPage { bp , routeItems , graph , tr . Range }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_routes_perf" , pi } )
2020-02-28 04:52:45 +00:00
}
2019-05-18 06:50:43 +00:00
func analyticsRowsToRefMap ( rows * sql . Rows ) ( map [ string ] int , error ) {
2018-05-27 09:36:35 +00:00
nameMap := make ( map [ string ] int )
defer rows . Close ( )
2021-05-03 01:53:05 +00:00
c . DebugDetail ( "name - count" )
2018-05-27 09:36:35 +00:00
for rows . Next ( ) {
var count int
var name string
2021-05-03 01:53:05 +00:00
e := rows . Scan ( & count , & name )
if e != nil {
return nameMap , e
2018-05-27 09:36:35 +00:00
}
// TODO: Bulk log this
2019-04-19 06:36:26 +00:00
if c . Dev . SuperDebug {
2021-05-03 01:53:05 +00:00
log . Print ( name , " - " , count )
2018-05-27 09:36:35 +00:00
}
nameMap [ name ] += count
}
return nameMap , rows . Err ( )
}
2019-02-23 06:29:19 +00:00
func analyticsRowsToDuoMap ( rows * sql . Rows , labelList [ ] int64 , viewMap map [ int64 ] int64 ) ( map [ string ] map [ int64 ] int64 , map [ string ] int , error ) {
vMap := make ( map [ string ] map [ int64 ] int64 )
nameMap := make ( map [ string ] int )
defer rows . Close ( )
for rows . Next ( ) {
var count int64
var name string
var createdAt time . Time
2021-05-03 01:53:05 +00:00
e := rows . Scan ( & count , & name , & createdAt )
if e != nil {
return vMap , nameMap , e
2019-02-23 06:29:19 +00:00
}
// TODO: Bulk log this
2019-10-11 00:57:33 +00:00
unixCreatedAt := createdAt . Unix ( )
2019-04-19 06:36:26 +00:00
if c . Dev . SuperDebug {
2019-02-23 06:29:19 +00:00
log . Print ( "count: " , count )
log . Print ( "name: " , name )
2021-05-03 01:53:05 +00:00
log . Print ( "createdAt: " , createdAt , " - " , unixCreatedAt )
2019-02-23 06:29:19 +00:00
}
2019-04-29 06:11:26 +00:00
2019-02-23 06:29:19 +00:00
vvMap , ok := vMap [ name ]
if ! ok {
vvMap = make ( map [ int64 ] int64 )
for key , val := range viewMap {
vvMap [ key ] = val
}
vMap [ name ] = vvMap
}
for _ , value := range labelList {
if unixCreatedAt > value {
vvMap [ value ] += count
break
}
}
nameMap [ name ] += int ( count )
}
return vMap , nameMap , rows . Err ( )
}
2019-02-24 01:29:06 +00:00
type OVItem struct {
name string
count int
viewMap map [ int64 ] int64
}
func analyticsVMapToOVList ( vMap map [ string ] map [ int64 ] int64 ) ( ovList [ ] OVItem ) {
// Order the map
2021-05-03 01:53:05 +00:00
ovList , i := make ( [ ] OVItem , len ( vMap ) ) , 0
2019-02-24 01:29:06 +00:00
for name , viewMap := range vMap {
var totcount int
for _ , count := range viewMap {
totcount += int ( count )
}
2021-05-03 01:53:05 +00:00
ovList [ i ] = OVItem { name , totcount , viewMap }
i ++
2019-02-24 01:29:06 +00:00
}
2020-02-28 04:52:45 +00:00
return sortOVList ( ovList )
2019-02-24 01:29:06 +00:00
}
2021-05-03 01:53:05 +00:00
func AnalyticsForums ( w http . ResponseWriter , r * http . Request , u * c . User ) c . RouteError {
bp , ferr := PreAnalyticsDetail ( w , r , u )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
bp . AddScript ( "chartist/chartist-plugin-legend.min.js" )
bp . AddSheet ( "chartist/chartist-plugin-legend.css" )
2019-02-24 01:29:06 +00:00
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , viewMap := c . AnalyticsTimeRangeToLabelList ( tr )
2018-05-27 09:36:35 +00:00
2021-05-03 01:53:05 +00:00
rows , e := qgen . NewAcc ( ) . Select ( "viewchunks_forums" ) . Columns ( "count,forum,createdAt" ) . Where ( "forum!=''" ) . DateCutoff ( "createdAt" , tr . Quantity , tr . Unit ) . Query ( )
if e != nil && e != sql . ErrNoRows {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
vMap , forumMap , e := analyticsRowsToDuoMap ( rows , labelList , viewMap )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2019-02-24 01:29:06 +00:00
ovList := analyticsVMapToOVList ( vMap )
var vList [ ] [ ] int64
var legendList [ ] string
var i int
for _ , ovitem := range ovList {
2021-05-03 01:53:05 +00:00
viewList := make ( [ ] int64 , len ( revLabelList ) )
for i , val := range revLabelList {
viewList [ i ] = ovitem . viewMap [ val ]
2019-02-24 01:29:06 +00:00
}
vList = append ( vList , viewList )
2021-05-03 01:53:05 +00:00
fid , e := strconv . Atoi ( ovitem . name )
if e != nil {
return c . InternalError ( e , w , r )
2019-02-24 01:29:06 +00:00
}
var lName string
2021-05-03 01:53:05 +00:00
forum , e := c . Forums . Get ( fid )
if e == sql . ErrNoRows {
lName = "Deleted Forum" // TODO: Localise this
} else if e != nil {
return c . InternalError ( e , w , r )
2019-02-24 01:29:06 +00:00
} else {
lName = forum . Name
}
legendList = append ( legendList , lName )
if i >= 6 {
break
}
i ++
}
2021-05-03 01:53:05 +00:00
graph := createTimeGraph ( vList , labelList , legendList )
2018-05-27 09:36:35 +00:00
// TODO: Sort this slice
2021-05-03 01:53:05 +00:00
forumItems , i := make ( [ ] c . PanelAnalyticsAgentsItem , len ( forumMap ) ) , 0
2018-05-27 09:36:35 +00:00
for sfid , count := range forumMap {
2021-05-03 01:53:05 +00:00
fid , e := strconv . Atoi ( sfid )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2019-02-24 01:29:06 +00:00
var lName string
2021-05-03 01:53:05 +00:00
forum , e := c . Forums . Get ( fid )
if e == sql . ErrNoRows {
2019-02-24 01:29:06 +00:00
// TODO: Localise this
lName = "Deleted Forum"
2021-05-03 01:53:05 +00:00
} else if e != nil {
return c . InternalError ( e , w , r )
2019-02-24 01:29:06 +00:00
} else {
lName = forum . Name
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
forumItems [ i ] = c . PanelAnalyticsAgentsItem {
2018-05-27 09:36:35 +00:00
Agent : sfid ,
2019-02-24 01:29:06 +00:00
FriendlyAgent : lName ,
2018-05-27 09:36:35 +00:00
Count : count ,
2021-05-03 01:53:05 +00:00
}
i ++
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsDuoPage { bp , forumItems , graph , tr . Range }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_forums" , pi } )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
func AnalyticsRoutes ( w http . ResponseWriter , r * http . Request , u * c . User ) c . RouteError {
bp , ferr := PreAnalyticsDetail ( w , r , u )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
bp . AddScript ( "chartist/chartist-plugin-legend.min.js" )
bp . AddSheet ( "chartist/chartist-plugin-legend.css" )
2019-02-24 01:29:06 +00:00
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , viewMap := c . AnalyticsTimeRangeToLabelList ( tr )
2018-05-27 09:36:35 +00:00
2021-05-03 01:53:05 +00:00
rows , e := qgen . NewAcc ( ) . Select ( "viewchunks" ) . Columns ( "count,route,createdAt" ) . Where ( "route!=''" ) . DateCutoff ( "createdAt" , tr . Quantity , tr . Unit ) . Query ( )
if e != nil && e != sql . ErrNoRows {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
vMap , routeMap , e := analyticsRowsToDuoMap ( rows , labelList , viewMap )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2020-02-28 04:52:45 +00:00
//c.DebugLogf("vMap: %+v\n", vMap)
//c.DebugLogf("routeMap: %+v\n", routeMap)
2019-02-24 01:29:06 +00:00
ovList := analyticsVMapToOVList ( vMap )
2020-02-28 04:52:45 +00:00
//c.DebugLogf("ovList: %+v\n", ovList)
2019-02-24 01:29:06 +00:00
2019-05-18 06:50:43 +00:00
ex := strings . Split ( r . FormValue ( "ex" ) , "," )
2019-10-11 00:57:33 +00:00
inEx := func ( name string ) bool {
2019-05-18 06:50:43 +00:00
for _ , e := range ex {
if e == name {
return true
}
}
return false
}
2019-02-24 01:29:06 +00:00
var vList [ ] [ ] int64
var legendList [ ] string
var i int
for _ , ovitem := range ovList {
2019-05-18 06:50:43 +00:00
if inEx ( ovitem . name ) {
continue
}
2021-05-03 01:53:05 +00:00
viewList := make ( [ ] int64 , len ( revLabelList ) )
for i , val := range revLabelList {
viewList [ i ] = ovitem . viewMap [ val ]
2019-02-24 01:29:06 +00:00
}
vList = append ( vList , viewList )
2020-02-29 07:34:38 +00:00
shortName := strings . Replace ( ovitem . name , "routes." , "r." , - 1 )
legendList = append ( legendList , shortName )
if i >= 7 {
2019-02-24 01:29:06 +00:00
break
}
i ++
}
2021-05-03 01:53:05 +00:00
graph := createTimeGraph ( vList , labelList , legendList )
2018-05-27 09:36:35 +00:00
// TODO: Sort this slice
2019-04-19 06:36:26 +00:00
var routeItems [ ] c . PanelAnalyticsRoutesItem
2018-05-27 09:36:35 +00:00
for route , count := range routeMap {
2019-05-18 06:50:43 +00:00
if inEx ( route ) {
continue
}
2019-04-19 06:36:26 +00:00
routeItems = append ( routeItems , c . PanelAnalyticsRoutesItem {
2018-05-27 09:36:35 +00:00
Route : route ,
Count : count ,
} )
}
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsRoutesPage { bp , routeItems , graph , tr . Range }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_routes" , pi } )
2018-05-27 09:36:35 +00:00
}
2019-02-23 06:29:19 +00:00
// Trialling multi-series charts
2021-05-03 01:53:05 +00:00
func AnalyticsAgents ( w http . ResponseWriter , r * http . Request , u * c . User ) c . RouteError {
bp , ferr := PreAnalyticsDetail ( w , r , u )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
bp . AddScript ( "chartist/chartist-plugin-legend.min.js" )
bp . AddSheet ( "chartist/chartist-plugin-legend.css" )
2019-02-23 06:29:19 +00:00
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , viewMap := c . AnalyticsTimeRangeToLabelList ( tr )
2018-05-27 09:36:35 +00:00
2021-05-03 01:53:05 +00:00
rows , e := qgen . NewAcc ( ) . Select ( "viewchunks_agents" ) . Columns ( "count,browser,createdAt" ) . DateCutoff ( "createdAt" , tr . Quantity , tr . Unit ) . Query ( )
if e != nil && e != sql . ErrNoRows {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
vMap , agentMap , e := analyticsRowsToDuoMap ( rows , labelList , viewMap )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2019-02-24 01:29:06 +00:00
ovList := analyticsVMapToOVList ( vMap )
2019-02-23 06:29:19 +00:00
2019-06-05 04:57:10 +00:00
ex := strings . Split ( r . FormValue ( "ex" ) , "," )
2019-10-11 00:57:33 +00:00
inEx := func ( name string ) bool {
2019-06-05 04:57:10 +00:00
for _ , e := range ex {
if e == name {
return true
}
}
return false
}
2019-02-23 06:29:19 +00:00
var vList [ ] [ ] int64
var legendList [ ] string
var i int
for _ , ovitem := range ovList {
2019-06-05 04:57:10 +00:00
if inEx ( ovitem . name ) {
continue
2019-02-23 06:29:19 +00:00
}
2019-10-11 00:57:33 +00:00
lName , ok := p . GetUserAgentPhrase ( ovitem . name )
2019-02-23 06:29:19 +00:00
if ! ok {
lName = ovitem . name
}
2019-06-05 04:57:10 +00:00
if inEx ( lName ) {
continue
}
2021-05-03 01:53:05 +00:00
viewList := make ( [ ] int64 , len ( revLabelList ) )
for i , val := range revLabelList {
viewList [ i ] = ovitem . viewMap [ val ]
2019-06-05 04:57:10 +00:00
}
vList = append ( vList , viewList )
2019-02-23 06:29:19 +00:00
legendList = append ( legendList , lName )
2020-03-24 02:07:30 +00:00
if i >= 7 {
2019-02-23 06:29:19 +00:00
break
}
i ++
}
2021-05-03 01:53:05 +00:00
graph := createTimeGraph ( vList , labelList , legendList )
2019-02-23 06:29:19 +00:00
2018-05-27 09:36:35 +00:00
// TODO: Sort this slice
2019-04-19 06:36:26 +00:00
var agentItems [ ] c . PanelAnalyticsAgentsItem
2018-05-27 09:36:35 +00:00
for agent , count := range agentMap {
2019-06-05 04:57:10 +00:00
if inEx ( agent ) {
continue
}
2019-10-11 00:57:33 +00:00
aAgent , ok := p . GetUserAgentPhrase ( agent )
2018-05-27 09:36:35 +00:00
if ! ok {
aAgent = agent
}
2019-06-05 04:57:10 +00:00
if inEx ( aAgent ) {
continue
}
2019-04-19 06:36:26 +00:00
agentItems = append ( agentItems , c . PanelAnalyticsAgentsItem {
2018-05-27 09:36:35 +00:00
Agent : agent ,
FriendlyAgent : aAgent ,
Count : count ,
} )
}
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsDuoPage { bp , agentItems , graph , tr . Range }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_agents" , pi } )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
func AnalyticsSystems ( w http . ResponseWriter , r * http . Request , u * c . User ) c . RouteError {
bp , ferr := PreAnalyticsDetail ( w , r , u )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
bp . AddScript ( "chartist/chartist-plugin-legend.min.js" )
bp . AddSheet ( "chartist/chartist-plugin-legend.css" )
2019-02-24 01:29:06 +00:00
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , viewMap := c . AnalyticsTimeRangeToLabelList ( tr )
2018-05-27 09:36:35 +00:00
2021-05-03 01:53:05 +00:00
rows , e := qgen . NewAcc ( ) . Select ( "viewchunks_systems" ) . Columns ( "count,system,createdAt" ) . DateCutoff ( "createdAt" , tr . Quantity , tr . Unit ) . Query ( )
if e != nil && e != sql . ErrNoRows {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
vMap , osMap , e := analyticsRowsToDuoMap ( rows , labelList , viewMap )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2019-02-24 01:29:06 +00:00
ovList := analyticsVMapToOVList ( vMap )
var vList [ ] [ ] int64
var legendList [ ] string
var i int
for _ , ovitem := range ovList {
2021-05-03 01:53:05 +00:00
viewList := make ( [ ] int64 , len ( revLabelList ) )
for ii , val := range revLabelList {
viewList [ ii ] = ovitem . viewMap [ val ]
2019-02-24 01:29:06 +00:00
}
vList = append ( vList , viewList )
2019-10-11 00:57:33 +00:00
lName , ok := p . GetOSPhrase ( ovitem . name )
2019-02-24 01:29:06 +00:00
if ! ok {
lName = ovitem . name
}
legendList = append ( legendList , lName )
if i >= 6 {
break
}
i ++
}
2021-05-03 01:53:05 +00:00
graph := createTimeGraph ( vList , labelList , legendList )
2018-05-27 09:36:35 +00:00
// TODO: Sort this slice
2021-05-03 01:53:05 +00:00
systemItems , i := make ( [ ] c . PanelAnalyticsAgentsItem , len ( osMap ) ) , 0
2018-05-27 09:36:35 +00:00
for system , count := range osMap {
2019-10-11 00:57:33 +00:00
sSystem , ok := p . GetOSPhrase ( system )
2018-05-27 09:36:35 +00:00
if ! ok {
sSystem = system
}
2021-05-03 01:53:05 +00:00
systemItems [ i ] = c . PanelAnalyticsAgentsItem {
2018-05-27 09:36:35 +00:00
Agent : system ,
FriendlyAgent : sSystem ,
Count : count ,
2021-05-03 01:53:05 +00:00
}
i ++
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsDuoPage { bp , systemItems , graph , tr . Range }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_systems" , pi } )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
func AnalyticsLanguages ( w http . ResponseWriter , r * http . Request , u * c . User ) c . RouteError {
bp , ferr := PreAnalyticsDetail ( w , r , u )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
bp . AddScript ( "chartist/chartist-plugin-legend.min.js" )
bp . AddSheet ( "chartist/chartist-plugin-legend.css" )
2019-02-24 01:29:06 +00:00
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
revLabelList , labelList , viewMap := c . AnalyticsTimeRangeToLabelList ( tr )
2018-05-27 09:36:35 +00:00
2021-05-03 01:53:05 +00:00
rows , e := qgen . NewAcc ( ) . Select ( "viewchunks_langs" ) . Columns ( "count,lang,createdAt" ) . DateCutoff ( "createdAt" , tr . Quantity , tr . Unit ) . Query ( )
if e != nil && e != sql . ErrNoRows {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
vMap , langMap , e := analyticsRowsToDuoMap ( rows , labelList , viewMap )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2019-02-24 01:29:06 +00:00
ovList := analyticsVMapToOVList ( vMap )
2019-05-19 01:01:11 +00:00
ex := strings . Split ( r . FormValue ( "ex" ) , "," )
2019-09-30 10:15:50 +00:00
inEx := func ( name string ) bool {
2019-05-19 01:01:11 +00:00
for _ , e := range ex {
if e == name {
return true
}
}
return false
}
2019-02-24 01:29:06 +00:00
var vList [ ] [ ] int64
var legendList [ ] string
var i int
for _ , ovitem := range ovList {
2019-05-19 01:01:11 +00:00
if inEx ( ovitem . name ) {
continue
2019-02-24 01:29:06 +00:00
}
2019-10-11 00:57:33 +00:00
lName , ok := p . GetHumanLangPhrase ( ovitem . name )
2019-02-24 01:29:06 +00:00
if ! ok {
lName = ovitem . name
}
2019-05-19 01:01:11 +00:00
if inEx ( lName ) {
continue
}
2021-05-03 01:53:05 +00:00
viewList := make ( [ ] int64 , len ( revLabelList ) )
for _ , val := range revLabelList {
viewList [ i ] = ovitem . viewMap [ val ]
2019-05-19 01:01:11 +00:00
}
vList = append ( vList , viewList )
2019-02-24 01:29:06 +00:00
legendList = append ( legendList , lName )
if i >= 6 {
break
}
i ++
}
2021-05-03 01:53:05 +00:00
graph := createTimeGraph ( vList , labelList , legendList )
2018-05-27 09:36:35 +00:00
// TODO: Can we de-duplicate these analytics functions further?
// TODO: Sort this slice
2019-04-19 06:36:26 +00:00
var langItems [ ] c . PanelAnalyticsAgentsItem
2018-05-27 09:36:35 +00:00
for lang , count := range langMap {
2019-05-19 01:01:11 +00:00
if inEx ( lang ) {
continue
}
2019-10-11 00:57:33 +00:00
lLang , ok := p . GetHumanLangPhrase ( lang )
2018-05-27 09:36:35 +00:00
if ! ok {
lLang = lang
}
2019-05-19 01:01:11 +00:00
if inEx ( lLang ) {
continue
}
2019-04-19 06:36:26 +00:00
langItems = append ( langItems , c . PanelAnalyticsAgentsItem {
2018-05-27 09:36:35 +00:00
Agent : lang ,
FriendlyAgent : lLang ,
Count : count ,
} )
}
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsDuoPage { bp , langItems , graph , tr . Range }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_langs" , pi } )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
func AnalyticsReferrers ( w http . ResponseWriter , r * http . Request , u * c . User ) c . RouteError {
bp , ferr := buildBasePage ( w , r , u , "analytics" , "analytics" )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2021-05-03 01:53:05 +00:00
tr , e := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if e != nil {
return c . LocalError ( e . Error ( ) , w , r , u )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
rows , e := qgen . NewAcc ( ) . Select ( "viewchunks_referrers" ) . Columns ( "count,domain" ) . DateCutoff ( "createdAt" , tr . Quantity , tr . Unit ) . Query ( )
if e != nil && e != sql . ErrNoRows {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2021-05-03 01:53:05 +00:00
refMap , e := analyticsRowsToRefMap ( rows )
if e != nil {
return c . InternalError ( e , w , r )
2018-05-27 09:36:35 +00:00
}
2019-05-18 00:33:35 +00:00
showSpam := r . FormValue ( "spam" ) == "1"
2019-09-30 10:15:50 +00:00
isSpammy := func ( domain string ) bool {
2019-05-18 01:18:19 +00:00
for _ , substr := range c . SpammyDomainBits {
2019-05-18 00:33:35 +00:00
if strings . Contains ( domain , substr ) {
return true
}
}
return false
}
2018-05-27 09:36:35 +00:00
// TODO: Sort this slice
2019-04-19 06:36:26 +00:00
var refItems [ ] c . PanelAnalyticsAgentsItem
2018-05-27 09:36:35 +00:00
for domain , count := range refMap {
2019-05-18 00:33:35 +00:00
sdomain := c . SanitiseSingleLine ( domain )
if ! showSpam && isSpammy ( sdomain ) {
continue
}
2019-04-19 06:36:26 +00:00
refItems = append ( refItems , c . PanelAnalyticsAgentsItem {
2019-05-18 00:33:35 +00:00
Agent : sdomain ,
2018-05-27 09:36:35 +00:00
Count : count ,
} )
}
2021-05-03 01:53:05 +00:00
pi := c . PanelAnalyticsReferrersPage { bp , refItems , tr . Range , showSpam }
return renderTemplate ( "panel" , w , r , bp . Header , c . Panel { bp , "panel_analytics_right" , "analytics" , "panel_analytics_referrers" , pi } )
2018-05-27 09:36:35 +00:00
}