01a692ab5b
Added tests for the word filter store. Added qgen.NewAcc() to reduce the amount of boilerplate needed for creating an accumulator. Exposed the RecordError method on the accumulator. Added an Add method to PluginList and removed AddPlugin() in favour of that. More panel buttons on Nox should be styled now. Added the panel_update_button_text phrase for future use. More errors might be caught in the thumbnailer now. Removed ls from .travis.yml, it was there for debugging Code Climate.
711 lines
23 KiB
Go
711 lines
23 KiB
Go
package panel
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"../../common"
|
|
"../../query_gen/lib"
|
|
)
|
|
|
|
// TODO: Move this to another file, probably common/pages.go
|
|
type AnalyticsTimeRange struct {
|
|
Quantity int
|
|
Unit string
|
|
Slices int
|
|
SliceWidth int
|
|
Range string
|
|
}
|
|
|
|
func analyticsTimeRange(rawTimeRange string) (timeRange AnalyticsTimeRange, err error) {
|
|
timeRange.Quantity = 6
|
|
timeRange.Unit = "hour"
|
|
timeRange.Slices = 12
|
|
timeRange.SliceWidth = 60 * 30
|
|
timeRange.Range = "six-hours"
|
|
|
|
switch rawTimeRange {
|
|
case "one-month":
|
|
timeRange.Quantity = 30
|
|
timeRange.Unit = "day"
|
|
timeRange.Slices = 30
|
|
timeRange.SliceWidth = 60 * 60 * 24
|
|
timeRange.Range = "one-month"
|
|
case "one-week":
|
|
timeRange.Quantity = 7
|
|
timeRange.Unit = "day"
|
|
timeRange.Slices = 14
|
|
timeRange.SliceWidth = 60 * 60 * 12
|
|
timeRange.Range = "one-week"
|
|
case "two-days": // Two days is experimental
|
|
timeRange.Quantity = 2
|
|
timeRange.Unit = "day"
|
|
timeRange.Slices = 24
|
|
timeRange.SliceWidth = 60 * 60 * 2
|
|
timeRange.Range = "two-days"
|
|
case "one-day":
|
|
timeRange.Quantity = 1
|
|
timeRange.Unit = "day"
|
|
timeRange.Slices = 24
|
|
timeRange.SliceWidth = 60 * 60
|
|
timeRange.Range = "one-day"
|
|
case "twelve-hours":
|
|
timeRange.Quantity = 12
|
|
timeRange.Slices = 24
|
|
timeRange.Range = "twelve-hours"
|
|
case "six-hours", "":
|
|
timeRange.Range = "six-hours"
|
|
default:
|
|
return timeRange, errors.New("Unknown time range")
|
|
}
|
|
return timeRange, nil
|
|
}
|
|
|
|
func analyticsTimeRangeToLabelList(timeRange AnalyticsTimeRange) (revLabelList []int64, labelList []int64, viewMap map[int64]int64) {
|
|
viewMap = make(map[int64]int64)
|
|
var currentTime = time.Now().Unix()
|
|
for i := 1; i <= timeRange.Slices; i++ {
|
|
var label = currentTime - int64(i*timeRange.SliceWidth)
|
|
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
|
|
}
|
|
|
|
var unixCreatedAt = createdAt.Unix()
|
|
// TODO: Bulk log this
|
|
if common.Dev.SuperDebug {
|
|
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()
|
|
}
|
|
|
|
func PreAnalyticsDetail(w http.ResponseWriter, r *http.Request, user *common.User) (*common.BasePanelPage, common.RouteError) {
|
|
basePage, ferr := buildBasePage(w, r, user, "analytics", "analytics")
|
|
if ferr != nil {
|
|
return nil, ferr
|
|
}
|
|
|
|
basePage.AddSheet("chartist/chartist.min.css")
|
|
basePage.AddScript("chartist/chartist.min.js")
|
|
basePage.AddScript("analytics.js")
|
|
return basePage, nil
|
|
}
|
|
|
|
func AnalyticsViews(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
|
|
|
common.DebugLog("in panel.AnalyticsViews")
|
|
// TODO: Add some sort of analytics store / iterator?
|
|
rows, err := qgen.NewAcc().Select("viewchunks").Columns("count, createdAt").Where("route = ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
viewMap, err = analyticsRowsToViewMap(rows, labelList, viewMap)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
var viewList []int64
|
|
var viewItems []common.PanelAnalyticsItem
|
|
for _, value := range revLabelList {
|
|
viewList = append(viewList, viewMap[value])
|
|
viewItems = append(viewItems, common.PanelAnalyticsItem{Time: value, Count: viewMap[value]})
|
|
}
|
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
|
common.DebugLogf("graph: %+v\n", graph)
|
|
|
|
pi := common.PanelAnalyticsPage{basePage, graph, viewItems, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_views", w, r, user, &pi)
|
|
}
|
|
|
|
func AnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user common.User, route string) common.RouteError {
|
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
|
|
|
common.DebugLog("in panel.AnalyticsRouteViews")
|
|
// TODO: Validate the route is valid
|
|
rows, err := qgen.NewAcc().Select("viewchunks").Columns("count, createdAt").Where("route = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(route)
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
viewMap, err = analyticsRowsToViewMap(rows, labelList, viewMap)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
var viewList []int64
|
|
var viewItems []common.PanelAnalyticsItem
|
|
for _, value := range revLabelList {
|
|
viewList = append(viewList, viewMap[value])
|
|
viewItems = append(viewItems, common.PanelAnalyticsItem{Time: value, Count: viewMap[value]})
|
|
}
|
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
|
common.DebugLogf("graph: %+v\n", graph)
|
|
|
|
pi := common.PanelAnalyticsRoutePage{basePage, common.SanitiseSingleLine(route), graph, viewItems, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_route_views", w, r, user, &pi)
|
|
}
|
|
|
|
func AnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user common.User, agent string) common.RouteError {
|
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
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
|
|
agent = common.SanitiseSingleLine(agent)
|
|
|
|
common.DebugLog("in panel.AnalyticsAgentViews")
|
|
// TODO: Verify the agent is valid
|
|
rows, err := qgen.NewAcc().Select("viewchunks_agents").Columns("count, createdAt").Where("browser = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(agent)
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
viewMap, err = analyticsRowsToViewMap(rows, labelList, viewMap)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
var viewList []int64
|
|
for _, value := range revLabelList {
|
|
viewList = append(viewList, viewMap[value])
|
|
}
|
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
|
common.DebugLogf("graph: %+v\n", graph)
|
|
|
|
friendlyAgent, ok := common.GetUserAgentPhrase(agent)
|
|
if !ok {
|
|
friendlyAgent = agent
|
|
}
|
|
|
|
pi := common.PanelAnalyticsAgentPage{basePage, agent, friendlyAgent, graph, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_agent_views", w, r, user, &pi)
|
|
}
|
|
|
|
func AnalyticsForumViews(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
|
|
|
fid, err := strconv.Atoi(sfid)
|
|
if err != nil {
|
|
return common.LocalError("Invalid integer", w, r, user)
|
|
}
|
|
|
|
common.DebugLog("in panel.AnalyticsForumViews")
|
|
// TODO: Verify the agent is valid
|
|
rows, err := qgen.NewAcc().Select("viewchunks_forums").Columns("count, createdAt").Where("forum = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(fid)
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
viewMap, err = analyticsRowsToViewMap(rows, labelList, viewMap)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
var viewList []int64
|
|
for _, value := range revLabelList {
|
|
viewList = append(viewList, viewMap[value])
|
|
}
|
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
|
common.DebugLogf("graph: %+v\n", graph)
|
|
|
|
forum, err := common.Forums.Get(fid)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
pi := common.PanelAnalyticsAgentPage{basePage, sfid, forum.Name, graph, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_forum_views", w, r, user, &pi)
|
|
}
|
|
|
|
func AnalyticsSystemViews(w http.ResponseWriter, r *http.Request, user common.User, system string) common.RouteError {
|
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
|
system = common.SanitiseSingleLine(system)
|
|
|
|
common.DebugLog("in panel.AnalyticsSystemViews")
|
|
// TODO: Verify the OS name is valid
|
|
rows, err := qgen.NewAcc().Select("viewchunks_systems").Columns("count, createdAt").Where("system = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(system)
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
viewMap, err = analyticsRowsToViewMap(rows, labelList, viewMap)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
var viewList []int64
|
|
for _, value := range revLabelList {
|
|
viewList = append(viewList, viewMap[value])
|
|
}
|
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
|
common.DebugLogf("graph: %+v\n", graph)
|
|
|
|
friendlySystem, ok := common.GetOSPhrase(system)
|
|
if !ok {
|
|
friendlySystem = system
|
|
}
|
|
|
|
pi := common.PanelAnalyticsAgentPage{basePage, system, friendlySystem, graph, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_system_views", w, r, user, &pi)
|
|
}
|
|
|
|
func AnalyticsLanguageViews(w http.ResponseWriter, r *http.Request, user common.User, lang string) common.RouteError {
|
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
|
lang = common.SanitiseSingleLine(lang)
|
|
|
|
common.DebugLog("in panel.AnalyticsLanguageViews")
|
|
// TODO: Verify the language code is valid
|
|
rows, err := qgen.NewAcc().Select("viewchunks_langs").Columns("count, createdAt").Where("lang = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(lang)
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
viewMap, err = analyticsRowsToViewMap(rows, labelList, viewMap)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
var viewList []int64
|
|
for _, value := range revLabelList {
|
|
viewList = append(viewList, viewMap[value])
|
|
}
|
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
|
common.DebugLogf("graph: %+v\n", graph)
|
|
|
|
friendlyLang, ok := common.GetHumanLangPhrase(lang)
|
|
if !ok {
|
|
friendlyLang = lang
|
|
}
|
|
|
|
pi := common.PanelAnalyticsAgentPage{basePage, lang, friendlyLang, graph, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_lang_views", w, r, user, &pi)
|
|
}
|
|
|
|
func AnalyticsReferrerViews(w http.ResponseWriter, r *http.Request, user common.User, domain string) common.RouteError {
|
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
|
|
|
common.DebugLog("in panel.AnalyticsReferrerViews")
|
|
// TODO: Verify the agent is valid
|
|
rows, err := qgen.NewAcc().Select("viewchunks_referrers").Columns("count, createdAt").Where("domain = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(domain)
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
viewMap, err = analyticsRowsToViewMap(rows, labelList, viewMap)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
var viewList []int64
|
|
for _, value := range revLabelList {
|
|
viewList = append(viewList, viewMap[value])
|
|
}
|
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
|
common.DebugLogf("graph: %+v\n", graph)
|
|
|
|
pi := common.PanelAnalyticsAgentPage{basePage, common.SanitiseSingleLine(domain), "", graph, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_referrer_views", w, r, user, &pi)
|
|
}
|
|
|
|
func AnalyticsTopics(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
|
|
|
common.DebugLog("in panel.AnalyticsTopics")
|
|
rows, err := qgen.NewAcc().Select("topicchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
viewMap, err = analyticsRowsToViewMap(rows, labelList, viewMap)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
var viewList []int64
|
|
var viewItems []common.PanelAnalyticsItem
|
|
for _, value := range revLabelList {
|
|
viewList = append(viewList, viewMap[value])
|
|
viewItems = append(viewItems, common.PanelAnalyticsItem{Time: value, Count: viewMap[value]})
|
|
}
|
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
|
common.DebugLogf("graph: %+v\n", graph)
|
|
|
|
pi := common.PanelAnalyticsPage{basePage, graph, viewItems, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_topics", w, r, user, &pi)
|
|
}
|
|
|
|
func AnalyticsPosts(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
|
|
|
common.DebugLog("in panel.AnalyticsPosts")
|
|
rows, err := qgen.NewAcc().Select("postchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
viewMap, err = analyticsRowsToViewMap(rows, labelList, viewMap)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
var viewList []int64
|
|
var viewItems []common.PanelAnalyticsItem
|
|
for _, value := range revLabelList {
|
|
viewList = append(viewList, viewMap[value])
|
|
viewItems = append(viewItems, common.PanelAnalyticsItem{Time: value, Count: viewMap[value]})
|
|
}
|
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
|
common.DebugLogf("graph: %+v\n", graph)
|
|
|
|
pi := common.PanelAnalyticsPage{basePage, graph, viewItems, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_posts", w, r, user, &pi)
|
|
}
|
|
|
|
func analyticsRowsToNameMap(rows *sql.Rows) (map[string]int, error) {
|
|
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
|
|
if common.Dev.SuperDebug {
|
|
log.Print("count: ", count)
|
|
log.Print("name: ", name)
|
|
}
|
|
nameMap[name] += count
|
|
}
|
|
return nameMap, rows.Err()
|
|
}
|
|
|
|
func AnalyticsForums(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
|
basePage, ferr := buildBasePage(w, r, &user, "analytics", "analytics")
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
|
|
rows, err := qgen.NewAcc().Select("viewchunks_forums").Columns("count, forum").Where("forum != ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
forumMap, err := analyticsRowsToNameMap(rows)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
// TODO: Sort this slice
|
|
var forumItems []common.PanelAnalyticsAgentsItem
|
|
for sfid, count := range forumMap {
|
|
fid, err := strconv.Atoi(sfid)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
forum, err := common.Forums.Get(fid)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
forumItems = append(forumItems, common.PanelAnalyticsAgentsItem{
|
|
Agent: sfid,
|
|
FriendlyAgent: forum.Name,
|
|
Count: count,
|
|
})
|
|
}
|
|
|
|
pi := common.PanelAnalyticsAgentsPage{basePage, forumItems, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_forums", w, r, user, &pi)
|
|
}
|
|
|
|
func AnalyticsRoutes(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
|
basePage, ferr := buildBasePage(w, r, &user, "analytics", "analytics")
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
|
|
rows, err := qgen.NewAcc().Select("viewchunks").Columns("count, route").Where("route != ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
routeMap, err := analyticsRowsToNameMap(rows)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
// TODO: Sort this slice
|
|
var routeItems []common.PanelAnalyticsRoutesItem
|
|
for route, count := range routeMap {
|
|
routeItems = append(routeItems, common.PanelAnalyticsRoutesItem{
|
|
Route: route,
|
|
Count: count,
|
|
})
|
|
}
|
|
|
|
pi := common.PanelAnalyticsRoutesPage{basePage, routeItems, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_routes", w, r, user, &pi)
|
|
}
|
|
|
|
func AnalyticsAgents(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
|
basePage, ferr := buildBasePage(w, r, &user, "analytics", "analytics")
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
|
|
rows, err := qgen.NewAcc().Select("viewchunks_agents").Columns("count, browser").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
agentMap, err := analyticsRowsToNameMap(rows)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
// TODO: Sort this slice
|
|
var agentItems []common.PanelAnalyticsAgentsItem
|
|
for agent, count := range agentMap {
|
|
aAgent, ok := common.GetUserAgentPhrase(agent)
|
|
if !ok {
|
|
aAgent = agent
|
|
}
|
|
agentItems = append(agentItems, common.PanelAnalyticsAgentsItem{
|
|
Agent: agent,
|
|
FriendlyAgent: aAgent,
|
|
Count: count,
|
|
})
|
|
}
|
|
|
|
pi := common.PanelAnalyticsAgentsPage{basePage, agentItems, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_agents", w, r, user, &pi)
|
|
}
|
|
|
|
func AnalyticsSystems(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
|
basePage, ferr := buildBasePage(w, r, &user, "analytics", "analytics")
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
|
|
rows, err := qgen.NewAcc().Select("viewchunks_systems").Columns("count, system").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
osMap, err := analyticsRowsToNameMap(rows)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
// TODO: Sort this slice
|
|
var systemItems []common.PanelAnalyticsAgentsItem
|
|
for system, count := range osMap {
|
|
sSystem, ok := common.GetOSPhrase(system)
|
|
if !ok {
|
|
sSystem = system
|
|
}
|
|
systemItems = append(systemItems, common.PanelAnalyticsAgentsItem{
|
|
Agent: system,
|
|
FriendlyAgent: sSystem,
|
|
Count: count,
|
|
})
|
|
}
|
|
|
|
pi := common.PanelAnalyticsAgentsPage{basePage, systemItems, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_systems", w, r, user, &pi)
|
|
}
|
|
|
|
func AnalyticsLanguages(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
|
basePage, ferr := buildBasePage(w, r, &user, "analytics", "analytics")
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
|
|
rows, err := qgen.NewAcc().Select("viewchunks_langs").Columns("count, lang").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
langMap, err := analyticsRowsToNameMap(rows)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
// TODO: Can we de-duplicate these analytics functions further?
|
|
// TODO: Sort this slice
|
|
var langItems []common.PanelAnalyticsAgentsItem
|
|
for lang, count := range langMap {
|
|
lLang, ok := common.GetHumanLangPhrase(lang)
|
|
if !ok {
|
|
lLang = lang
|
|
}
|
|
langItems = append(langItems, common.PanelAnalyticsAgentsItem{
|
|
Agent: lang,
|
|
FriendlyAgent: lLang,
|
|
Count: count,
|
|
})
|
|
}
|
|
|
|
pi := common.PanelAnalyticsAgentsPage{basePage, langItems, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_langs", w, r, user, &pi)
|
|
}
|
|
|
|
func AnalyticsReferrers(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
|
basePage, ferr := buildBasePage(w, r, &user, "analytics", "analytics")
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
|
if err != nil {
|
|
return common.LocalError(err.Error(), w, r, user)
|
|
}
|
|
|
|
rows, err := qgen.NewAcc().Select("viewchunks_referrers").Columns("count, domain").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
refMap, err := analyticsRowsToNameMap(rows)
|
|
if err != nil {
|
|
return common.InternalError(err, w, r)
|
|
}
|
|
|
|
// TODO: Sort this slice
|
|
var refItems []common.PanelAnalyticsAgentsItem
|
|
for domain, count := range refMap {
|
|
refItems = append(refItems, common.PanelAnalyticsAgentsItem{
|
|
Agent: common.SanitiseSingleLine(domain),
|
|
Count: count,
|
|
})
|
|
}
|
|
|
|
pi := common.PanelAnalyticsAgentsPage{basePage, refItems, timeRange.Range}
|
|
return panelRenderTemplate("panel_analytics_referrers", w, r, user, &pi)
|
|
}
|