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
)
// TODO: Move this to another file, probably common/pages.go
type AnalyticsTimeRange struct {
Quantity int
Unit string
Slices int
SliceWidth int
Range string
}
2019-04-27 06:32:26 +00:00
func analyticsTimeRange ( rawTimeRange string ) ( * AnalyticsTimeRange , error ) {
2019-09-29 05:10:05 +00:00
tRange := & 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" :
2019-09-29 05:10:05 +00:00
tRange . Quantity = 12
tRange . Unit = "month"
tRange . Slices = 12
tRange . SliceWidth = 60 * 60 * 24 * 30
tRange . Range = "one-year"
2019-02-23 06:29:19 +00:00
case "three-months" :
2019-09-29 05:10:05 +00:00
tRange . Quantity = 90
tRange . Unit = "day"
tRange . Slices = 30
tRange . SliceWidth = 60 * 60 * 24 * 3
tRange . Range = "three-months"
2018-05-27 09:36:35 +00:00
case "one-month" :
2019-09-29 05:10:05 +00:00
tRange . Quantity = 30
tRange . Unit = "day"
tRange . Slices = 30
tRange . SliceWidth = 60 * 60 * 24
tRange . Range = "one-month"
2018-05-27 09:36:35 +00:00
case "one-week" :
2019-09-29 05:10:05 +00:00
tRange . Quantity = 7
tRange . Unit = "day"
tRange . Slices = 14
tRange . SliceWidth = 60 * 60 * 12
tRange . Range = "one-week"
2018-05-27 09:36:35 +00:00
case "two-days" : // Two days is experimental
2019-09-29 05:10:05 +00:00
tRange . Quantity = 2
tRange . Unit = "day"
tRange . Slices = 24
tRange . SliceWidth = 60 * 60 * 2
tRange . Range = "two-days"
2018-05-27 09:36:35 +00:00
case "one-day" :
2019-09-29 05:10:05 +00:00
tRange . Quantity = 1
tRange . Unit = "day"
tRange . Slices = 24
tRange . SliceWidth = 60 * 60
tRange . Range = "one-day"
2018-05-27 09:36:35 +00:00
case "twelve-hours" :
2019-09-29 05:10:05 +00:00
tRange . Quantity = 12
tRange . Slices = 24
tRange . Range = "twelve-hours"
2018-05-27 09:36:35 +00:00
case "six-hours" , "" :
default :
2019-09-29 05:10:05 +00:00
return tRange , errors . New ( "Unknown time range" )
2018-05-27 09:36:35 +00:00
}
2019-09-29 05:10:05 +00:00
return tRange , nil
2018-05-27 09:36:35 +00:00
}
2020-02-28 04:52:45 +00:00
// TODO: Clamp it rather than using an offset off the current time to avoid chaotic changes in stats as adjacent sets converge and diverge?
2019-04-27 06:32:26 +00:00
func analyticsTimeRangeToLabelList ( timeRange * AnalyticsTimeRange ) ( revLabelList [ ] int64 , labelList [ ] int64 , viewMap map [ int64 ] int64 ) {
2018-05-27 09:36:35 +00:00
viewMap = make ( map [ int64 ] int64 )
2019-09-29 05:10:05 +00:00
currentTime := time . Now ( ) . Unix ( )
2018-05-27 09:36:35 +00:00
for i := 1 ; i <= timeRange . Slices ; i ++ {
2019-09-29 05:10:05 +00:00
label := currentTime - int64 ( i * timeRange . SliceWidth )
2018-05-27 09:36:35 +00:00
revLabelList = append ( revLabelList , label )
viewMap [ label ] = 0
}
for _ , value := range revLabelList {
labelList = append ( labelList , value )
}
return revLabelList , labelList , viewMap
}
func analyticsRowsToViewMap ( rows * sql . Rows , labelList [ ] int64 , viewMap map [ int64 ] int64 ) ( map [ int64 ] int64 , error ) {
defer rows . Close ( )
for rows . Next ( ) {
var count int64
var createdAt time . Time
err := rows . Scan ( & count , & createdAt )
if err != nil {
return viewMap , err
}
2019-09-29 05:10:05 +00:00
unixCreatedAt := createdAt . Unix ( )
2018-05-27 09:36:35 +00:00
// TODO: Bulk log this
2019-04-19 06:36:26 +00:00
if c . Dev . SuperDebug {
2018-05-27 09:36:35 +00:00
log . Print ( "count: " , count )
log . Print ( "createdAt: " , createdAt )
log . Print ( "unixCreatedAt: " , unixCreatedAt )
}
for _ , value := range labelList {
if unixCreatedAt > value {
viewMap [ value ] += count
break
}
}
}
return viewMap , rows . Err ( )
}
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
err := rows . Scan ( & count , & createdAt )
if err != nil {
return avgMap , err
}
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 )
log . Print ( "createdAt: " , createdAt )
log . Print ( "unixCreatedAt: " , unixCreatedAt )
}
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
err := rows . Scan ( & stack , & heap , & createdAt )
if err != nil {
return avgMap , err
}
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 )
log . Print ( "createdAt: " , createdAt )
log . Print ( "unixCreatedAt: " , unixCreatedAt )
}
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
err := rows . Scan ( & low , & high , & avg , & createdAt )
if err != nil {
return avgMap , err
}
unixCreatedAt := createdAt . Unix ( )
// TODO: Bulk log this
if c . Dev . SuperDebug {
log . Print ( "low: " , low )
log . Print ( "high: " , high )
log . Print ( "avg: " , avg )
log . Print ( "createdAt: " , createdAt )
log . Print ( "unixCreatedAt: " , unixCreatedAt )
}
var dat int64
switch typ {
case 0 :
dat = low
case 1 :
dat = high
default :
dat = avg
}
pAvgMap := make ( map [ int64 ] pAvg )
for _ , value := range labelList {
if unixCreatedAt > value {
prev := pAvgMap [ value ]
prev . Avg += dat
prev . Tot ++
pAvgMap [ value ] = prev
break
}
}
for key , pAvg := range pAvgMap {
avgMap [ key ] = pAvg . Avg / pAvg . Tot
}
}
return avgMap , rows . Err ( )
}
2019-04-19 06:36:26 +00:00
func PreAnalyticsDetail ( w http . ResponseWriter , r * http . Request , user * c . User ) ( * c . BasePanelPage , c . RouteError ) {
2019-10-11 00:57:33 +00:00
bp , ferr := buildBasePage ( w , r , user , "analytics" , "analytics" )
2018-06-06 00:21:22 +00:00
if ferr != nil {
return nil , ferr
}
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
}
2020-03-18 09:21:34 +00:00
func AnalyticsViews ( w http . ResponseWriter , r * http . Request , user * c . User ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
revLabelList , labelList , viewMap := analyticsTimeRangeToLabelList ( timeRange )
2019-04-19 06:36:26 +00:00
c . DebugLog ( "in panel.AnalyticsViews" )
2018-07-31 04:02:09 +00:00
// TODO: Add some sort of analytics store / iterator?
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "viewchunks" ) . Columns ( "count,createdAt" ) . Where ( "route=''" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
viewMap , err = analyticsRowsToViewMap ( rows , labelList , viewMap )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
2020-02-23 09:08:47 +00:00
viewList := make ( [ ] int64 , len ( revLabelList ) )
viewItems := make ( [ ] c . PanelAnalyticsItem , len ( revLabelList ) )
2019-07-28 23:19:46 +00:00
for i , value := range revLabelList {
viewList [ i ] = viewMap [ value ]
viewItems [ i ] = c . PanelAnalyticsItem { Time : value , Count : viewMap [ value ] }
2018-05-27 09:36:35 +00:00
}
2020-02-23 09:08:47 +00:00
2019-04-19 06:36:26 +00:00
graph := c . PanelTimeGraph { Series : [ ] [ ] int64 { viewList } , Labels : labelList }
c . DebugLogf ( "graph: %+v\n" , graph )
2019-02-24 01:29:06 +00:00
var ttime string
if timeRange . Range == "six-hours" || timeRange . Range == "twelve-hours" || timeRange . Range == "one-day" {
ttime = "time"
}
2019-04-29 06:11:26 +00:00
pi := c . PanelAnalyticsStd { graph , viewItems , timeRange . Range , timeRange . Unit , ttime }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_views" , pi } )
2018-05-27 09:36:35 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsRouteViews ( w http . ResponseWriter , r * http . Request , user * c . User , route string ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
revLabelList , labelList , viewMap := analyticsTimeRangeToLabelList ( timeRange )
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
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "viewchunks" ) . Columns ( "count,createdAt" ) . Where ( "route=?" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( route )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
viewMap , err = analyticsRowsToViewMap ( rows , labelList , viewMap )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
var viewList [ ] int64
2019-04-19 06:36:26 +00:00
var viewItems [ ] c . PanelAnalyticsItem
2018-05-27 09:36:35 +00:00
for _ , value := range revLabelList {
viewList = append ( viewList , viewMap [ value ] )
2019-04-19 06:36:26 +00:00
viewItems = append ( viewItems , c . PanelAnalyticsItem { Time : value , Count : viewMap [ value ] } )
2018-05-27 09:36:35 +00:00
}
2019-04-19 06:36:26 +00:00
graph := c . PanelTimeGraph { Series : [ ] [ ] int64 { viewList } , Labels : labelList }
c . DebugLogf ( "graph: %+v\n" , graph )
2018-05-27 09:36:35 +00:00
2019-04-19 06:36:26 +00:00
pi := c . PanelAnalyticsRoutePage { basePage , c . SanitiseSingleLine ( route ) , graph , viewItems , timeRange . Range }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_route_views" , pi } )
2018-05-27 09:36:35 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsAgentViews ( w http . ResponseWriter , r * http . Request , user * c . User , agent string ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
revLabelList , labelList , viewMap := analyticsTimeRangeToLabelList ( timeRange )
// ? 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
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "viewchunks_agents" ) . Columns ( "count,createdAt" ) . Where ( "browser=?" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( agent )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
viewMap , err = analyticsRowsToViewMap ( rows , labelList , viewMap )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
var viewList [ ] int64
for _ , value := range revLabelList {
viewList = append ( viewList , viewMap [ value ] )
}
2019-04-19 06:36:26 +00:00
graph := c . PanelTimeGraph { Series : [ ] [ ] int64 { viewList } , Labels : labelList }
c . DebugLogf ( "graph: %+v\n" , graph )
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
}
2019-04-19 06:36:26 +00:00
pi := c . PanelAnalyticsAgentPage { basePage , agent , friendlyAgent , graph , timeRange . Range }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_agent_views" , pi } )
2018-05-27 09:36:35 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsForumViews ( w http . ResponseWriter , r * http . Request , user * c . User , sfid string ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
revLabelList , labelList , viewMap := analyticsTimeRangeToLabelList ( timeRange )
fid , err := strconv . Atoi ( sfid )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( "Invalid integer" , w , r , user )
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
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "viewchunks_forums" ) . Columns ( "count,createdAt" ) . Where ( "forum=?" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( fid )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
viewMap , err = analyticsRowsToViewMap ( rows , labelList , viewMap )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
var viewList [ ] int64
for _ , value := range revLabelList {
viewList = append ( viewList , viewMap [ value ] )
}
2019-04-19 06:36:26 +00:00
graph := c . PanelTimeGraph { Series : [ ] [ ] int64 { viewList } , Labels : labelList }
c . DebugLogf ( "graph: %+v\n" , graph )
2018-05-27 09:36:35 +00:00
2019-04-19 06:36:26 +00:00
forum , err := c . Forums . Get ( fid )
2018-05-27 09:36:35 +00:00
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
2019-04-19 06:36:26 +00:00
pi := c . PanelAnalyticsAgentPage { basePage , sfid , forum . Name , graph , timeRange . Range }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_forum_views" , pi } )
2018-05-27 09:36:35 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsSystemViews ( w http . ResponseWriter , r * http . Request , user * c . User , system string ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
revLabelList , labelList , viewMap := analyticsTimeRangeToLabelList ( timeRange )
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
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "viewchunks_systems" ) . Columns ( "count,createdAt" ) . Where ( "system=?" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( system )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
viewMap , err = analyticsRowsToViewMap ( rows , labelList , viewMap )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
var viewList [ ] int64
for _ , value := range revLabelList {
viewList = append ( viewList , viewMap [ value ] )
}
2019-04-19 06:36:26 +00:00
graph := c . PanelTimeGraph { Series : [ ] [ ] int64 { viewList } , Labels : labelList }
c . DebugLogf ( "graph: %+v\n" , graph )
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
}
2019-04-19 06:36:26 +00:00
pi := c . PanelAnalyticsAgentPage { basePage , system , friendlySystem , graph , timeRange . Range }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_system_views" , pi } )
2018-05-27 09:36:35 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsLanguageViews ( w http . ResponseWriter , r * http . Request , user * c . User , lang string ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
revLabelList , labelList , viewMap := analyticsTimeRangeToLabelList ( timeRange )
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
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "viewchunks_langs" ) . Columns ( "count,createdAt" ) . Where ( "lang=?" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( lang )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
viewMap , err = analyticsRowsToViewMap ( rows , labelList , viewMap )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
var viewList [ ] int64
for _ , value := range revLabelList {
viewList = append ( viewList , viewMap [ value ] )
}
2019-04-19 06:36:26 +00:00
graph := c . PanelTimeGraph { Series : [ ] [ ] int64 { viewList } , Labels : labelList }
c . DebugLogf ( "graph: %+v\n" , graph )
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
}
2019-04-19 06:36:26 +00:00
pi := c . PanelAnalyticsAgentPage { basePage , lang , friendlyLang , graph , timeRange . Range }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_lang_views" , pi } )
2018-05-27 09:36:35 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsReferrerViews ( w http . ResponseWriter , r * http . Request , user * c . User , domain string ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
revLabelList , labelList , viewMap := analyticsTimeRangeToLabelList ( timeRange )
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
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "viewchunks_referrers" ) . Columns ( "count,createdAt" ) . Where ( "domain=?" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( domain )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
viewMap , err = analyticsRowsToViewMap ( rows , labelList , viewMap )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
var viewList [ ] int64
for _ , value := range revLabelList {
viewList = append ( viewList , viewMap [ value ] )
}
2019-04-19 06:36:26 +00:00
graph := c . PanelTimeGraph { Series : [ ] [ ] int64 { viewList } , Labels : labelList }
c . DebugLogf ( "graph: %+v\n" , graph )
pi := c . PanelAnalyticsAgentPage { basePage , c . SanitiseSingleLine ( domain ) , "" , graph , timeRange . Range }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_referrer_views" , pi } )
2018-05-27 09:36:35 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsTopics ( w http . ResponseWriter , r * http . Request , user * c . User ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
revLabelList , labelList , viewMap := analyticsTimeRangeToLabelList ( timeRange )
2019-04-19 06:36:26 +00:00
c . DebugLog ( "in panel.AnalyticsTopics" )
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "topicchunks" ) . Columns ( "count,createdAt" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
viewMap , err = analyticsRowsToViewMap ( rows , labelList , viewMap )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
var viewList [ ] int64
2019-04-19 06:36:26 +00:00
var viewItems [ ] c . PanelAnalyticsItem
2018-05-27 09:36:35 +00:00
for _ , value := range revLabelList {
viewList = append ( viewList , viewMap [ value ] )
2019-04-19 06:36:26 +00:00
viewItems = append ( viewItems , c . PanelAnalyticsItem { Time : value , Count : viewMap [ value ] } )
2018-05-27 09:36:35 +00:00
}
2019-04-19 06:36:26 +00:00
graph := c . PanelTimeGraph { Series : [ ] [ ] int64 { viewList } , Labels : labelList }
c . DebugLogf ( "graph: %+v\n" , graph )
2019-04-29 06:11:26 +00:00
pi := c . PanelAnalyticsStd { graph , viewItems , timeRange . Range , timeRange . Unit , "time" }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_topics" , pi } )
2018-05-27 09:36:35 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsPosts ( w http . ResponseWriter , r * http . Request , user * c . User ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
revLabelList , labelList , viewMap := analyticsTimeRangeToLabelList ( timeRange )
2019-04-19 06:36:26 +00:00
c . DebugLog ( "in panel.AnalyticsPosts" )
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "postchunks" ) . Columns ( "count,createdAt" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
viewMap , err = analyticsRowsToViewMap ( rows , labelList , viewMap )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
var viewList [ ] int64
2019-04-19 06:36:26 +00:00
var viewItems [ ] c . PanelAnalyticsItem
2018-05-27 09:36:35 +00:00
for _ , value := range revLabelList {
viewList = append ( viewList , viewMap [ value ] )
2019-04-19 06:36:26 +00:00
viewItems = append ( viewItems , c . PanelAnalyticsItem { Time : value , Count : viewMap [ value ] } )
2018-05-27 09:36:35 +00:00
}
2019-04-19 06:36:26 +00:00
graph := c . PanelTimeGraph { Series : [ ] [ ] int64 { viewList } , Labels : labelList }
c . DebugLogf ( "graph: %+v\n" , graph )
2019-04-29 06:11:26 +00:00
pi := c . PanelAnalyticsStd { graph , viewItems , timeRange . Range , timeRange . Unit , "time" }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_posts" , pi } )
2018-05-27 09:36:35 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsMemory ( w http . ResponseWriter , r * http . Request , user * c . User ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2019-05-01 06:59:51 +00:00
if ferr != nil {
return ferr
}
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
return c . LocalError ( err . Error ( ) , w , r , user )
}
2019-05-01 23:14:07 +00:00
revLabelList , labelList , avgMap := analyticsTimeRangeToLabelList ( timeRange )
2019-05-01 06:59:51 +00:00
c . DebugLog ( "in panel.AnalyticsMemory" )
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "memchunks" ) . Columns ( "count,createdAt" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( )
2019-05-01 06:59:51 +00:00
if err != nil && err != sql . ErrNoRows {
return c . InternalError ( err , w , r )
}
2019-05-01 23:14:07 +00:00
avgMap , err = analyticsRowsToAverageMap ( rows , labelList , avgMap )
2019-05-01 06:59:51 +00:00
if err != nil {
return c . InternalError ( err , w , r )
}
// TODO: Adjust for the missing chunks in week and month
2019-05-01 23:14:07 +00:00
var avgList [ ] int64
var avgItems [ ] c . PanelAnalyticsItemUnit
2019-05-01 06:59:51 +00:00
for _ , value := range revLabelList {
2019-05-01 23:14:07 +00:00
avgList = append ( avgList , avgMap [ value ] )
cv , cu := c . ConvertByteUnit ( float64 ( avgMap [ value ] ) )
avgItems = append ( avgItems , c . PanelAnalyticsItemUnit { Time : value , Unit : cu , Count : int64 ( cv ) } )
2019-05-01 06:59:51 +00:00
}
2019-05-01 23:14:07 +00:00
graph := c . PanelTimeGraph { Series : [ ] [ ] int64 { avgList } , Labels : labelList }
2019-05-01 06:59:51 +00:00
c . DebugLogf ( "graph: %+v\n" , graph )
2019-05-01 23:14:07 +00:00
pi := c . PanelAnalyticsStdUnit { graph , avgItems , timeRange . Range , timeRange . Unit , "time" }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "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
2020-03-18 09:21:34 +00:00
func AnalyticsActiveMemory ( w http . ResponseWriter , r * http . Request , user * c . User ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2019-05-09 06:58:55 +00:00
if ferr != nil {
return ferr
}
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
return c . LocalError ( err . Error ( ) , w , r , user )
}
revLabelList , labelList , avgMap := analyticsTimeRangeToLabelList ( timeRange )
c . DebugLog ( "in panel.AnalyticsActiveMemory" )
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "memchunks" ) . Columns ( "stack,heap,createdAt" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( )
2019-05-09 06:58:55 +00:00
if err != nil && err != sql . ErrNoRows {
return c . InternalError ( err , w , r )
}
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
}
avgMap , err = analyticsRowsToAverageMap2 ( rows , labelList , avgMap , typ )
2019-05-09 06:58:55 +00:00
if err != nil {
return c . InternalError ( err , w , r )
}
// TODO: Adjust for the missing chunks in week and month
var avgList [ ] int64
var avgItems [ ] c . PanelAnalyticsItemUnit
for _ , value := range revLabelList {
avgList = append ( avgList , avgMap [ value ] )
cv , cu := c . ConvertByteUnit ( float64 ( avgMap [ value ] ) )
avgItems = append ( avgItems , c . PanelAnalyticsItemUnit { Time : value , Unit : cu , Count : int64 ( cv ) } )
}
graph := c . PanelTimeGraph { Series : [ ] [ ] int64 { avgList } , Labels : labelList }
c . DebugLogf ( "graph: %+v\n" , graph )
2019-05-19 01:01:11 +00:00
pi := c . PanelAnalyticsActiveMemory { graph , avgItems , timeRange . Range , timeRange . Unit , "time" , typ }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_active_memory" , pi } )
2019-05-09 06:58:55 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsPerf ( w http . ResponseWriter , r * http . Request , user * c . User ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2020-02-23 09:08:47 +00:00
if ferr != nil {
return ferr
}
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
return c . LocalError ( err . Error ( ) , w , r , user )
}
revLabelList , labelList , avgMap := analyticsTimeRangeToLabelList ( timeRange )
c . DebugLog ( "in panel.AnalyticsPerf" )
rows , err := qgen . NewAcc ( ) . Select ( "perfchunks" ) . Columns ( "low,high,avg,createdAt" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( )
if err != nil && err != sql . ErrNoRows {
return c . InternalError ( err , w , r )
}
var typ int
switch r . FormValue ( "type" ) {
case "0" :
typ = 0
case "1" :
typ = 1
default :
typ = 2
}
avgMap , err = analyticsRowsToAverageMap3 ( rows , labelList , avgMap , typ )
if err != nil {
return c . InternalError ( err , w , r )
}
// TODO: Adjust for the missing chunks in week and month
var avgList [ ] int64
var avgItems [ ] c . PanelAnalyticsItemUnit
for _ , value := range revLabelList {
avgList = append ( avgList , avgMap [ value ] )
2020-02-24 08:28:43 +00:00
cv , cu := c . ConvertPerfUnit ( float64 ( avgMap [ value ] ) )
2020-02-23 09:08:47 +00:00
avgItems = append ( avgItems , c . PanelAnalyticsItemUnit { Time : value , Unit : cu , Count : int64 ( cv ) } )
}
graph := c . PanelTimeGraph { Series : [ ] [ ] int64 { avgList } , Labels : labelList }
c . DebugLogf ( "graph: %+v\n" , graph )
pi := c . PanelAnalyticsPerf { graph , avgItems , timeRange . Range , timeRange . Unit , "time" , typ }
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_performance" , pi } )
}
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
err := rows . Scan ( & count , & name , & createdAt )
if err != nil {
return aMap , nameMap , err
}
// TODO: Bulk log this
unixCreatedAt := createdAt . Unix ( )
if c . Dev . SuperDebug {
log . Print ( "count: " , count )
log . Print ( "name: " , name )
log . Print ( "createdAt: " , createdAt )
log . Print ( "unixCreatedAt: " , unixCreatedAt )
}
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 ( )
}
func sortOVList ( ovList [ ] OVItem ) ( tOVList [ ] OVItem ) {
// 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
for i := len ( ovList ) - 1 ; i >= 0 ; i -- {
tOVList = append ( tOVList , ovList [ i ] )
}
return tOVList
}
func analyticsAMapToOVList ( aMap map [ string ] map [ int64 ] int64 ) ( ovList [ ] OVItem ) {
// Order the map
for name , avgMap := range aMap {
var totcount int
for _ , count := range avgMap {
totcount = ( totcount + int ( count ) ) / 2
}
ovList = append ( ovList , OVItem { name , totcount , avgMap } )
}
return sortOVList ( ovList )
}
2020-03-18 09:21:34 +00:00
func AnalyticsRoutesPerf ( w http . ResponseWriter , r * http . Request , user * c . User ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2020-02-28 04:52:45 +00:00
if ferr != nil {
return ferr
}
basePage . AddScript ( "chartist/chartist-plugin-legend.min.js" )
basePage . AddSheet ( "chartist/chartist-plugin-legend.css" )
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
return c . LocalError ( err . Error ( ) , w , r , user )
}
// avgMap contains timestamps but not the averages for those stamps
revLabelList , labelList , avgMap := analyticsTimeRangeToLabelList ( timeRange )
rows , err := qgen . NewAcc ( ) . Select ( "viewchunks" ) . Columns ( "avg,route,createdAt" ) . Where ( "count!=0 AND route!=''" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( )
if err != nil && err != sql . ErrNoRows {
return c . InternalError ( err , w , r )
}
aMap , routeMap , err := analyticsRowsToAvgDuoMap ( rows , labelList , avgMap )
if err != nil {
return c . InternalError ( err , w , r )
}
//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
}
2020-02-28 04:52:45 +00:00
var viewList [ ] int64
for _ , value := range revLabelList {
viewList = append ( viewList , ovitem . viewMap [ value ] )
}
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 ++
}
graph := c . PanelTimeGraph { Series : vList , Labels : labelList , Legends : legendList }
c . DebugLogf ( "graph: %+v\n" , graph )
// 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 ) ,
} )
}
pi := c . PanelAnalyticsRoutesPerfPage { basePage , routeItems , graph , timeRange . Range }
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_routes_perf" , pi } )
}
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 ( )
for rows . Next ( ) {
var count int
var name string
err := rows . Scan ( & count , & name )
if err != nil {
return nameMap , err
}
// TODO: Bulk log this
2019-04-19 06:36:26 +00:00
if c . Dev . SuperDebug {
2018-05-27 09:36:35 +00:00
log . Print ( "count: " , count )
log . Print ( "name: " , name )
}
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
err := rows . Scan ( & count , & name , & createdAt )
if err != nil {
return vMap , nameMap , err
}
// 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 )
log . Print ( "createdAt: " , createdAt )
log . Print ( "unixCreatedAt: " , unixCreatedAt )
}
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
for name , viewMap := range vMap {
var totcount int
for _ , count := range viewMap {
totcount += int ( count )
}
ovList = append ( ovList , OVItem { name , totcount , viewMap } )
}
2020-02-28 04:52:45 +00:00
return sortOVList ( ovList )
2019-02-24 01:29:06 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsForums ( w http . ResponseWriter , r * http . Request , user * c . User ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2019-02-24 01:29:06 +00:00
basePage . AddScript ( "chartist/chartist-plugin-legend.min.js" )
basePage . AddSheet ( "chartist/chartist-plugin-legend.css" )
2018-05-27 09:36:35 +00:00
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
2019-02-24 01:29:06 +00:00
revLabelList , labelList , viewMap := analyticsTimeRangeToLabelList ( timeRange )
2018-05-27 09:36:35 +00:00
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "viewchunks_forums" ) . Columns ( "count,forum,createdAt" ) . Where ( "forum!=''" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
2019-02-24 01:29:06 +00:00
vMap , forumMap , err := analyticsRowsToDuoMap ( rows , labelList , viewMap )
2018-05-27 09:36:35 +00:00
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , 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 {
var viewList [ ] int64
for _ , value := range revLabelList {
viewList = append ( viewList , ovitem . viewMap [ value ] )
}
vList = append ( vList , viewList )
fid , err := strconv . Atoi ( ovitem . name )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2019-02-24 01:29:06 +00:00
}
var lName string
2019-04-19 06:36:26 +00:00
forum , err := c . Forums . Get ( fid )
2019-02-24 01:29:06 +00:00
if err == sql . ErrNoRows {
// TODO: Localise this
lName = "Deleted Forum"
} else if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2019-02-24 01:29:06 +00:00
} else {
lName = forum . Name
}
legendList = append ( legendList , lName )
if i >= 6 {
break
}
i ++
}
2019-04-19 06:36:26 +00:00
graph := c . PanelTimeGraph { Series : vList , Labels : labelList , Legends : legendList }
c . DebugLogf ( "graph: %+v\n" , graph )
2018-05-27 09:36:35 +00:00
// TODO: Sort this slice
2019-04-19 06:36:26 +00:00
var forumItems [ ] c . PanelAnalyticsAgentsItem
2018-05-27 09:36:35 +00:00
for sfid , count := range forumMap {
fid , err := strconv . Atoi ( sfid )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
2019-02-24 01:29:06 +00:00
var lName string
2019-04-19 06:36:26 +00:00
forum , err := c . Forums . Get ( fid )
2019-02-24 01:29:06 +00:00
if err == sql . ErrNoRows {
// TODO: Localise this
lName = "Deleted Forum"
} else if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2019-02-24 01:29:06 +00:00
} else {
lName = forum . Name
2018-05-27 09:36:35 +00:00
}
2019-04-19 06:36:26 +00:00
forumItems = append ( forumItems , 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 ,
} )
}
2019-04-19 06:36:26 +00:00
pi := c . PanelAnalyticsDuoPage { basePage , forumItems , graph , timeRange . Range }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_forums" , pi } )
2018-05-27 09:36:35 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsRoutes ( w http . ResponseWriter , r * http . Request , user * c . User ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2019-02-24 01:29:06 +00:00
basePage . AddScript ( "chartist/chartist-plugin-legend.min.js" )
basePage . AddSheet ( "chartist/chartist-plugin-legend.css" )
2018-05-27 09:36:35 +00:00
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
2019-02-24 01:29:06 +00:00
revLabelList , labelList , viewMap := analyticsTimeRangeToLabelList ( timeRange )
2018-05-27 09:36:35 +00:00
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "viewchunks" ) . Columns ( "count,route,createdAt" ) . Where ( "route!=''" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
2019-02-24 01:29:06 +00:00
vMap , routeMap , err := analyticsRowsToDuoMap ( rows , labelList , viewMap )
2018-05-27 09:36:35 +00:00
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , 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
}
2019-02-24 01:29:06 +00:00
var viewList [ ] int64
for _ , value := range revLabelList {
viewList = append ( viewList , ovitem . viewMap [ value ] )
}
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 ++
}
2019-04-19 06:36:26 +00:00
graph := c . PanelTimeGraph { Series : vList , Labels : labelList , Legends : legendList }
c . DebugLogf ( "graph: %+v\n" , graph )
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 ,
} )
}
2019-04-19 06:36:26 +00:00
pi := c . PanelAnalyticsRoutesPage { basePage , routeItems , graph , timeRange . Range }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "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
2020-03-18 09:21:34 +00:00
func AnalyticsAgents ( w http . ResponseWriter , r * http . Request , user * c . User ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2019-02-23 06:29:19 +00:00
basePage . AddScript ( "chartist/chartist-plugin-legend.min.js" )
basePage . AddSheet ( "chartist/chartist-plugin-legend.css" )
2018-05-27 09:36:35 +00:00
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
2019-02-23 06:29:19 +00:00
revLabelList , labelList , viewMap := analyticsTimeRangeToLabelList ( timeRange )
2018-05-27 09:36:35 +00:00
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "viewchunks_agents" ) . Columns ( "count,browser,createdAt" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
2019-02-23 06:29:19 +00:00
vMap , agentMap , err := analyticsRowsToDuoMap ( rows , labelList , viewMap )
2018-05-27 09:36:35 +00:00
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , 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
}
var viewList [ ] int64
for _ , value := range revLabelList {
viewList = append ( viewList , ovitem . viewMap [ value ] )
}
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 ++
}
2019-04-19 06:36:26 +00:00
graph := c . PanelTimeGraph { Series : vList , Labels : labelList , Legends : legendList }
c . DebugLogf ( "graph: %+v\n" , graph )
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 ,
} )
}
2019-04-19 06:36:26 +00:00
pi := c . PanelAnalyticsDuoPage { basePage , agentItems , graph , timeRange . Range }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_agents" , pi } )
2018-05-27 09:36:35 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsSystems ( w http . ResponseWriter , r * http . Request , user * c . User ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2019-02-24 01:29:06 +00:00
basePage . AddScript ( "chartist/chartist-plugin-legend.min.js" )
basePage . AddSheet ( "chartist/chartist-plugin-legend.css" )
2018-05-27 09:36:35 +00:00
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
2019-02-24 01:29:06 +00:00
revLabelList , labelList , viewMap := analyticsTimeRangeToLabelList ( timeRange )
2018-05-27 09:36:35 +00:00
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "viewchunks_systems" ) . Columns ( "count,system,createdAt" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
2019-02-24 01:29:06 +00:00
vMap , osMap , err := analyticsRowsToDuoMap ( rows , labelList , viewMap )
2018-05-27 09:36:35 +00:00
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , 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 {
var viewList [ ] int64
for _ , value := range revLabelList {
viewList = append ( viewList , ovitem . viewMap [ value ] )
}
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 ++
}
2019-04-19 06:36:26 +00:00
graph := c . PanelTimeGraph { Series : vList , Labels : labelList , Legends : legendList }
c . DebugLogf ( "graph: %+v\n" , graph )
2018-05-27 09:36:35 +00:00
// TODO: Sort this slice
2019-04-19 06:36:26 +00:00
var systemItems [ ] c . PanelAnalyticsAgentsItem
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
}
2019-04-19 06:36:26 +00:00
systemItems = append ( systemItems , c . PanelAnalyticsAgentsItem {
2018-05-27 09:36:35 +00:00
Agent : system ,
FriendlyAgent : sSystem ,
Count : count ,
} )
}
2019-04-19 06:36:26 +00:00
pi := c . PanelAnalyticsDuoPage { basePage , systemItems , graph , timeRange . Range }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_systems" , pi } )
2018-05-27 09:36:35 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsLanguages ( w http . ResponseWriter , r * http . Request , user * c . User ) c . RouteError {
basePage , ferr := PreAnalyticsDetail ( w , r , user )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
2019-02-24 01:29:06 +00:00
basePage . AddScript ( "chartist/chartist-plugin-legend.min.js" )
basePage . AddSheet ( "chartist/chartist-plugin-legend.css" )
2018-05-27 09:36:35 +00:00
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
2019-02-24 01:29:06 +00:00
revLabelList , labelList , viewMap := analyticsTimeRangeToLabelList ( timeRange )
2018-05-27 09:36:35 +00:00
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "viewchunks_langs" ) . Columns ( "count,lang,createdAt" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
2019-02-24 01:29:06 +00:00
vMap , langMap , err := analyticsRowsToDuoMap ( rows , labelList , viewMap )
2018-05-27 09:36:35 +00:00
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , 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
}
var viewList [ ] int64
for _ , value := range revLabelList {
viewList = append ( viewList , ovitem . viewMap [ value ] )
}
vList = append ( vList , viewList )
2019-02-24 01:29:06 +00:00
legendList = append ( legendList , lName )
if i >= 6 {
break
}
i ++
}
2019-04-19 06:36:26 +00:00
graph := c . PanelTimeGraph { Series : vList , Labels : labelList , Legends : legendList }
c . DebugLogf ( "graph: %+v\n" , graph )
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 ,
} )
}
2019-04-19 06:36:26 +00:00
pi := c . PanelAnalyticsDuoPage { basePage , langItems , graph , timeRange . Range }
2019-05-18 00:33:35 +00:00
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_langs" , pi } )
2018-05-27 09:36:35 +00:00
}
2020-03-18 09:21:34 +00:00
func AnalyticsReferrers ( w http . ResponseWriter , r * http . Request , user * c . User ) c . RouteError {
basePage , ferr := buildBasePage ( w , r , user , "analytics" , "analytics" )
2018-05-27 09:36:35 +00:00
if ferr != nil {
return ferr
}
timeRange , err := analyticsTimeRange ( r . FormValue ( "timeRange" ) )
if err != nil {
2019-04-19 06:36:26 +00:00
return c . LocalError ( err . Error ( ) , w , r , user )
2018-05-27 09:36:35 +00:00
}
2020-02-23 09:08:47 +00:00
rows , err := qgen . NewAcc ( ) . Select ( "viewchunks_referrers" ) . Columns ( "count,domain" ) . DateCutoff ( "createdAt" , timeRange . Quantity , timeRange . Unit ) . Query ( )
2018-05-27 09:36:35 +00:00
if err != nil && err != sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-05-27 09:36:35 +00:00
}
2019-05-18 06:50:43 +00:00
refMap , err := analyticsRowsToRefMap ( rows )
2018-05-27 09:36:35 +00:00
if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , 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 ,
} )
}
2019-05-18 00:33:35 +00:00
pi := c . PanelAnalyticsReferrersPage { basePage , refItems , timeRange . Range , showSpam }
return renderTemplate ( "panel" , w , r , basePage . Header , c . Panel { basePage , "panel_analytics_right" , "analytics" , "panel_analytics_referrers" , pi } )
2018-05-27 09:36:35 +00:00
}