bf851bd9fc
Added support for dyntmpl to the template system. The Account Dashboard now sort of uses dyntmpl, more work needed here. Renamed the pre_render_view_topic hook to pre_render_topic. Added the GetCurrentLangPack() function. Added the alerts_no_new_alerts phrase. Added the account_level_list phrase. Refactored the route rename logic in the patcher to cut down on the amount of boilerplate. Added more route renames to the patcher. You will need to run the patcher / updater in this commit.
711 lines
23 KiB
Go
711 lines
23 KiB
Go
package panel
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/Azareal/Gosora/common"
|
|
"github.com/Azareal/Gosora/query_gen"
|
|
)
|
|
|
|
// 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 renderTemplate("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 renderTemplate("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 renderTemplate("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 renderTemplate("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 renderTemplate("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 renderTemplate("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 renderTemplate("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 renderTemplate("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 renderTemplate("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 renderTemplate("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 renderTemplate("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 renderTemplate("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 renderTemplate("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 renderTemplate("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 renderTemplate("panel_analytics_referrers", w, r, user, &pi)
|
|
}
|