mirror of https://github.com/status-im/fathom.git
move remainder of aggregation funcs from count pkg to datastore pkg
This commit is contained in:
parent
94b5a03d8e
commit
b0d16b648b
|
@ -3,25 +3,36 @@ package api
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/usefathom/fathom/pkg/count"
|
"github.com/usefathom/fathom/pkg/datastore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// URL: /api/visitors/count
|
// URL: /api/visitors/count
|
||||||
var GetVisitorsCountHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
var GetVisitorsCountHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
before, after := getRequestedPeriods(r)
|
before, after := getRequestedPeriods(r)
|
||||||
result := count.Visitors(before, after)
|
result, err := datastore.TotalVisitors(before, after)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return respond(w, envelope{Data: result})
|
return respond(w, envelope{Data: result})
|
||||||
})
|
})
|
||||||
|
|
||||||
// URL: /api/visitors/count/realtime
|
// URL: /api/visitors/count/realtime
|
||||||
var GetVisitorsRealtimeCountHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
var GetVisitorsRealtimeCountHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
result := count.RealtimeVisitors()
|
result, err := datastore.RealtimeVisitors()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return respond(w, envelope{Data: result})
|
return respond(w, envelope{Data: result})
|
||||||
})
|
})
|
||||||
|
|
||||||
// URL: /api/visitors/count/group/:period
|
// URL: /api/visitors/count/group/:period
|
||||||
var GetVisitorsPeriodCountHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
var GetVisitorsPeriodCountHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
before, after := getRequestedPeriods(r)
|
before, after := getRequestedPeriods(r)
|
||||||
results := count.VisitorsPerDay(before, after)
|
results, err := datastore.TotalVisitorsPerDay(before, after)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return respond(w, envelope{Data: results})
|
return respond(w, envelope{Data: results})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,27 +1,12 @@
|
||||||
package count
|
package count
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/usefathom/fathom/pkg/datastore"
|
"github.com/usefathom/fathom/pkg/datastore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TotalUniqueBrowsers returns the total # of unique browsers between two given timestamps
|
|
||||||
func TotalUniqueBrowsers(before int64, after int64) int {
|
|
||||||
var total int
|
|
||||||
|
|
||||||
stmt, err := datastore.DB.Prepare(`
|
|
||||||
SELECT
|
|
||||||
IFNULL( SUM(t.count_unique), 0 )
|
|
||||||
FROM total_browser_names t
|
|
||||||
WHERE UNIX_TIMESTAMP(t.date) <= ? AND UNIX_TIMESTAMP(t.date) >= ?`)
|
|
||||||
checkError(err)
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
err = stmt.QueryRow(before, after).Scan(&total)
|
|
||||||
checkError(err)
|
|
||||||
|
|
||||||
return total
|
|
||||||
}
|
|
||||||
|
|
||||||
// Browsers returns a point slice containing browser data per browser name
|
// Browsers returns a point slice containing browser data per browser name
|
||||||
func Browsers(before int64, after int64, limit int) []Point {
|
func Browsers(before int64, after int64, limit int) []Point {
|
||||||
stmt, err := datastore.DB.Prepare(`
|
stmt, err := datastore.DB.Prepare(`
|
||||||
|
@ -40,7 +25,9 @@ func Browsers(before int64, after int64, limit int) []Point {
|
||||||
checkError(err)
|
checkError(err)
|
||||||
|
|
||||||
points := newPointSlice(rows)
|
points := newPointSlice(rows)
|
||||||
total := TotalUniqueBrowsers(before, after)
|
total, err := datastore.TotalUniqueBrowsers(before, after)
|
||||||
|
checkError(err)
|
||||||
|
|
||||||
points = calculatePointPercentages(points, total)
|
points = calculatePointPercentages(points, total)
|
||||||
|
|
||||||
return points
|
return points
|
||||||
|
@ -48,16 +35,14 @@ func Browsers(before int64, after int64, limit int) []Point {
|
||||||
|
|
||||||
// CreateBrowserTotals aggregates screen data into daily totals
|
// CreateBrowserTotals aggregates screen data into daily totals
|
||||||
func CreateBrowserTotals(since string) {
|
func CreateBrowserTotals(since string) {
|
||||||
rows := queryTotalRows(`
|
tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02")
|
||||||
SELECT
|
totals, err := datastore.BrowserCountPerDay(tomorrow, since)
|
||||||
v.browser_name,
|
if err != nil {
|
||||||
COUNT(*) AS count,
|
log.Fatal(err)
|
||||||
COUNT(DISTINCT(pv.visitor_id)) AS count_unique,
|
}
|
||||||
DATE_FORMAT(pv.timestamp, "%Y-%m-%d") AS date_group
|
|
||||||
FROM pageviews pv
|
|
||||||
LEFT JOIN visitors v ON v.id = pv.visitor_id
|
|
||||||
WHERE pv.timestamp > ?
|
|
||||||
GROUP BY date_group, v.browser_name`, since)
|
|
||||||
|
|
||||||
processTotalRows(rows, "total_browser_names")
|
err = datastore.SaveTotals("browser_names", totals)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,22 +2,13 @@ package count
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/usefathom/fathom/pkg/datastore"
|
"github.com/usefathom/fathom/pkg/datastore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Total represents a daily aggregated total for a metric
|
|
||||||
type Total struct {
|
|
||||||
ID int64
|
|
||||||
PageID int64
|
|
||||||
Value string
|
|
||||||
Count int64
|
|
||||||
CountUnique int64
|
|
||||||
Date string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Point represents a data point, will always have a Label and Value
|
// Point represents a data point, will always have a Label and Value
|
||||||
type Point struct {
|
type Point struct {
|
||||||
Label string
|
Label string
|
||||||
|
@ -27,6 +18,10 @@ type Point struct {
|
||||||
|
|
||||||
func getLastArchivedDate() string {
|
func getLastArchivedDate() string {
|
||||||
value, _ := datastore.GetOption("last_archived")
|
value, _ := datastore.GetOption("last_archived")
|
||||||
|
if value == "" {
|
||||||
|
return time.Now().AddDate(-1, 0, 0).Format("2006-01-02")
|
||||||
|
}
|
||||||
|
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,32 +42,6 @@ func Archive() {
|
||||||
log.Infof("finished aggregating metrics. ran for %dms.", (end.UnixNano()-start.UnixNano())/1000000)
|
log.Infof("finished aggregating metrics. ran for %dms.", (end.UnixNano()-start.UnixNano())/1000000)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the Total in the given database connection + table
|
|
||||||
func (t *Total) Save(Conn *sql.DB, table string) error {
|
|
||||||
stmt, err := datastore.DB.Prepare(`INSERT INTO ` + table + `(
|
|
||||||
value,
|
|
||||||
count,
|
|
||||||
count_unique,
|
|
||||||
date
|
|
||||||
) VALUES( ?, ?, ?, ? ) ON DUPLICATE KEY UPDATE count = ?, count_unique = ?`)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
result, err := stmt.Exec(
|
|
||||||
t.Value,
|
|
||||||
t.Count,
|
|
||||||
t.CountUnique,
|
|
||||||
t.Date,
|
|
||||||
t.Count,
|
|
||||||
t.CountUnique,
|
|
||||||
)
|
|
||||||
t.ID, _ = result.LastInsertId()
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkError(err error) {
|
func checkError(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -101,26 +70,3 @@ func calculatePointPercentages(points []Point, total int) []Point {
|
||||||
|
|
||||||
return points
|
return points
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryTotalRows(sql string, lastArchived string) *sql.Rows {
|
|
||||||
stmt, err := datastore.DB.Prepare(sql)
|
|
||||||
checkError(err)
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
rows, err := stmt.Query(lastArchived)
|
|
||||||
checkError(err)
|
|
||||||
return rows
|
|
||||||
}
|
|
||||||
|
|
||||||
func processTotalRows(rows *sql.Rows, table string) {
|
|
||||||
datastore.DB.Exec("START TRANSACTION")
|
|
||||||
for rows.Next() {
|
|
||||||
var t Total
|
|
||||||
err := rows.Scan(&t.Value, &t.Count, &t.CountUnique, &t.Date)
|
|
||||||
checkError(err)
|
|
||||||
t.Save(datastore.DB, table)
|
|
||||||
}
|
|
||||||
datastore.DB.Exec("COMMIT")
|
|
||||||
|
|
||||||
rows.Close()
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,30 +1,14 @@
|
||||||
package count
|
package count
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/usefathom/fathom/pkg/datastore"
|
"github.com/usefathom/fathom/pkg/datastore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TotalUniqueLanguages returns the total # of unique browser languages between two given timestamps
|
|
||||||
func TotalUniqueLanguages(before int64, after int64) int {
|
|
||||||
var total int
|
|
||||||
|
|
||||||
stmt, err := datastore.DB.Prepare(`
|
|
||||||
SELECT
|
|
||||||
IFNULL( SUM(t.count_unique), 0 )
|
|
||||||
FROM total_browser_languages t
|
|
||||||
WHERE UNIX_TIMESTAMP(t.date) <= ? AND UNIX_TIMESTAMP(t.date) >= ?`)
|
|
||||||
checkError(err)
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
err = stmt.QueryRow(before, after).Scan(&total)
|
|
||||||
checkError(err)
|
|
||||||
|
|
||||||
return total
|
|
||||||
}
|
|
||||||
|
|
||||||
// Languages returns a point slice containing language data per language
|
// Languages returns a point slice containing language data per language
|
||||||
func Languages(before int64, after int64, limit int) []Point {
|
func Languages(before int64, after int64, limit int) []Point {
|
||||||
// TODO: Calculate total instead of requiring it as a parameter.
|
|
||||||
stmt, err := datastore.DB.Prepare(`
|
stmt, err := datastore.DB.Prepare(`
|
||||||
SELECT
|
SELECT
|
||||||
t.value,
|
t.value,
|
||||||
|
@ -41,7 +25,9 @@ func Languages(before int64, after int64, limit int) []Point {
|
||||||
checkError(err)
|
checkError(err)
|
||||||
|
|
||||||
points := newPointSlice(rows)
|
points := newPointSlice(rows)
|
||||||
total := TotalUniqueLanguages(before, after)
|
total, err := datastore.TotalUniqueLanguages(before, after)
|
||||||
|
checkError(err)
|
||||||
|
|
||||||
points = calculatePointPercentages(points, total)
|
points = calculatePointPercentages(points, total)
|
||||||
|
|
||||||
return points
|
return points
|
||||||
|
@ -49,16 +35,14 @@ func Languages(before int64, after int64, limit int) []Point {
|
||||||
|
|
||||||
// CreateLanguageTotals aggregates screen data into daily totals
|
// CreateLanguageTotals aggregates screen data into daily totals
|
||||||
func CreateLanguageTotals(since string) {
|
func CreateLanguageTotals(since string) {
|
||||||
rows := queryTotalRows(`
|
tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02")
|
||||||
SELECT
|
totals, err := datastore.LanguageCountPerDay(tomorrow, since)
|
||||||
v.browser_language,
|
if err != nil {
|
||||||
COUNT(*) AS count,
|
log.Fatal(err)
|
||||||
COUNT(DISTINCT(pv.visitor_id)) AS count_unique,
|
}
|
||||||
DATE_FORMAT(pv.timestamp, "%Y-%m-%d") AS date_group
|
|
||||||
FROM pageviews pv
|
|
||||||
LEFT JOIN visitors v ON v.id = pv.visitor_id
|
|
||||||
WHERE pv.timestamp > ?
|
|
||||||
GROUP BY date_group, v.browser_language`, since)
|
|
||||||
|
|
||||||
processTotalRows(rows, "total_browser_languages")
|
err = datastore.SaveTotals("browser_languages", totals)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
package count
|
package count
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/usefathom/fathom/pkg/datastore"
|
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/usefathom/fathom/pkg/datastore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreatePageviewTotals aggregates pageview data for each page into daily totals
|
// CreatePageviewTotals aggregates pageview data for each page into daily totals
|
||||||
func CreatePageviewTotals(since string) {
|
func CreatePageviewTotals(since string) {
|
||||||
tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-02-01")
|
tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02")
|
||||||
totals, err := datastore.PageviewCountPerPageAndDay(tomorrow, since)
|
totals, err := datastore.PageviewCountPerPageAndDay(tomorrow, since)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
|
@ -1,27 +1,12 @@
|
||||||
package count
|
package count
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/usefathom/fathom/pkg/datastore"
|
"github.com/usefathom/fathom/pkg/datastore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TotalReferrers returns the total # of referrers between two given timestamps
|
|
||||||
func TotalReferrers(before int64, after int64) int {
|
|
||||||
var total int
|
|
||||||
|
|
||||||
stmt, err := datastore.DB.Prepare(`
|
|
||||||
SELECT
|
|
||||||
IFNULL( SUM(t.count), 0 )
|
|
||||||
FROM total_referrers t
|
|
||||||
WHERE UNIX_TIMESTAMP(t.date) <= ? AND UNIX_TIMESTAMP(t.date) >= ?`)
|
|
||||||
checkError(err)
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
err = stmt.QueryRow(before, after).Scan(&total)
|
|
||||||
checkError(err)
|
|
||||||
|
|
||||||
return total
|
|
||||||
}
|
|
||||||
|
|
||||||
// Referrers returns a point slice containing browser data per browser name
|
// Referrers returns a point slice containing browser data per browser name
|
||||||
func Referrers(before int64, after int64, limit int) []Point {
|
func Referrers(before int64, after int64, limit int) []Point {
|
||||||
stmt, err := datastore.DB.Prepare(`
|
stmt, err := datastore.DB.Prepare(`
|
||||||
|
@ -40,7 +25,9 @@ func Referrers(before int64, after int64, limit int) []Point {
|
||||||
checkError(err)
|
checkError(err)
|
||||||
|
|
||||||
points := newPointSlice(rows)
|
points := newPointSlice(rows)
|
||||||
total := TotalReferrers(before, after)
|
total, err := datastore.TotalReferrers(before, after)
|
||||||
|
checkError(err)
|
||||||
|
|
||||||
points = calculatePointPercentages(points, total)
|
points = calculatePointPercentages(points, total)
|
||||||
|
|
||||||
return points
|
return points
|
||||||
|
@ -48,17 +35,14 @@ func Referrers(before int64, after int64, limit int) []Point {
|
||||||
|
|
||||||
// CreateReferrerTotals aggregates screen data into daily totals
|
// CreateReferrerTotals aggregates screen data into daily totals
|
||||||
func CreateReferrerTotals(since string) {
|
func CreateReferrerTotals(since string) {
|
||||||
rows := queryTotalRows(`
|
tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02")
|
||||||
SELECT
|
totals, err := datastore.ReferrerCountPerDay(tomorrow, since)
|
||||||
pv.referrer_url,
|
if err != nil {
|
||||||
COUNT(*) AS count,
|
log.Fatal(err)
|
||||||
COUNT(DISTINCT(pv.visitor_id)) AS count_unique,
|
}
|
||||||
DATE_FORMAT(pv.timestamp, "%Y-%m-%d") AS date_group
|
|
||||||
FROM pageviews pv
|
|
||||||
WHERE pv.referrer_url IS NOT NULL
|
|
||||||
AND pv.referrer_url != ''
|
|
||||||
AND pv.timestamp > ?
|
|
||||||
GROUP BY date_group, pv.referrer_url`, since)
|
|
||||||
|
|
||||||
processTotalRows(rows, "total_referrers")
|
err = datastore.SaveTotals("referrers", totals)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,12 @@
|
||||||
package count
|
package count
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/usefathom/fathom/pkg/datastore"
|
"github.com/usefathom/fathom/pkg/datastore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TotalUniqueScreens returns the total # of screens between two given timestamps
|
|
||||||
func TotalUniqueScreens(before int64, after int64) int {
|
|
||||||
var total int
|
|
||||||
|
|
||||||
stmt, err := datastore.DB.Prepare(`
|
|
||||||
SELECT
|
|
||||||
IFNULL( SUM(t.count_unique), 0 )
|
|
||||||
FROM total_screens t
|
|
||||||
WHERE UNIX_TIMESTAMP(t.date) <= ? AND UNIX_TIMESTAMP(t.date) >= ?`)
|
|
||||||
checkError(err)
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
err = stmt.QueryRow(before, after).Scan(&total)
|
|
||||||
checkError(err)
|
|
||||||
|
|
||||||
return total
|
|
||||||
}
|
|
||||||
|
|
||||||
// Screens returns a point slice containing screen data per size
|
// Screens returns a point slice containing screen data per size
|
||||||
func Screens(before int64, after int64, limit int) []Point {
|
func Screens(before int64, after int64, limit int) []Point {
|
||||||
stmt, err := datastore.DB.Prepare(`
|
stmt, err := datastore.DB.Prepare(`
|
||||||
|
@ -40,7 +25,9 @@ func Screens(before int64, after int64, limit int) []Point {
|
||||||
checkError(err)
|
checkError(err)
|
||||||
|
|
||||||
points := newPointSlice(rows)
|
points := newPointSlice(rows)
|
||||||
total := TotalUniqueScreens(before, after)
|
total, err := datastore.TotalUniqueScreens(before, after)
|
||||||
|
checkError(err)
|
||||||
|
|
||||||
points = calculatePointPercentages(points, total)
|
points = calculatePointPercentages(points, total)
|
||||||
|
|
||||||
return points
|
return points
|
||||||
|
@ -48,16 +35,14 @@ func Screens(before int64, after int64, limit int) []Point {
|
||||||
|
|
||||||
// CreateScreenTotals aggregates screen data into daily totals
|
// CreateScreenTotals aggregates screen data into daily totals
|
||||||
func CreateScreenTotals(since string) {
|
func CreateScreenTotals(since string) {
|
||||||
rows := queryTotalRows(`
|
tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02")
|
||||||
SELECT
|
totals, err := datastore.ScreenCountPerDay(tomorrow, since)
|
||||||
v.screen_resolution,
|
if err != nil {
|
||||||
COUNT(*) AS count,
|
log.Fatal(err)
|
||||||
COUNT(DISTINCT(pv.visitor_id)) AS count_unique,
|
}
|
||||||
DATE_FORMAT(pv.timestamp, "%Y-%m-%d") AS date_group
|
|
||||||
FROM pageviews pv
|
|
||||||
LEFT JOIN visitors v ON v.id = pv.visitor_id
|
|
||||||
WHERE pv.timestamp > ?
|
|
||||||
GROUP BY date_group, v.screen_resolution`, since)
|
|
||||||
|
|
||||||
processTotalRows(rows, "total_screens")
|
err = datastore.SaveTotals("screens", totals)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,95 +1,23 @@
|
||||||
package count
|
package count
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/usefathom/fathom/pkg/datastore"
|
"github.com/usefathom/fathom/pkg/datastore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RealtimeVisitors returns the total number of visitors in the last 3 minutes
|
|
||||||
func RealtimeVisitors() int {
|
|
||||||
var result int
|
|
||||||
datastore.DB.QueryRow(`
|
|
||||||
SELECT COUNT(DISTINCT(pv.visitor_id))
|
|
||||||
FROM pageviews pv
|
|
||||||
WHERE pv.timestamp >= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 3 HOUR_MINUTE) AND pv.timestamp <= CURRENT_TIMESTAMP`).Scan(&result)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Visitors returns the number of total visitors between the given timestamps
|
|
||||||
func Visitors(before int64, after int64) int {
|
|
||||||
// get total
|
|
||||||
stmt, err := datastore.DB.Prepare(`
|
|
||||||
SELECT
|
|
||||||
SUM(t.count)
|
|
||||||
FROM total_visitors t
|
|
||||||
WHERE UNIX_TIMESTAMP(t.date) <= ? AND UNIX_TIMESTAMP(t.date) >= ?`)
|
|
||||||
checkError(err)
|
|
||||||
defer stmt.Close()
|
|
||||||
var total int
|
|
||||||
stmt.QueryRow(before, after).Scan(&total)
|
|
||||||
return total
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitorsPerDay returns a point slice containing visitor data per day
|
|
||||||
func VisitorsPerDay(before int64, after int64) []Point {
|
|
||||||
stmt, err := datastore.DB.Prepare(`SELECT
|
|
||||||
SUM(t.count) AS count,
|
|
||||||
DATE_FORMAT(t.date, '%Y-%m-%d') AS date_group
|
|
||||||
FROM total_visitors t
|
|
||||||
WHERE UNIX_TIMESTAMP(t.date) <= ? AND UNIX_TIMESTAMP(t.date) >= ?
|
|
||||||
GROUP BY date_group`)
|
|
||||||
checkError(err)
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
rows, err := stmt.Query(before, after)
|
|
||||||
checkError(err)
|
|
||||||
|
|
||||||
var results []Point
|
|
||||||
defer rows.Close()
|
|
||||||
for rows.Next() {
|
|
||||||
p := Point{}
|
|
||||||
err = rows.Scan(&p.Value, &p.Label)
|
|
||||||
checkError(err)
|
|
||||||
results = append(results, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateVisitorTotals aggregates visitor data into daily totals
|
// CreateVisitorTotals aggregates visitor data into daily totals
|
||||||
func CreateVisitorTotals(since string) {
|
func CreateVisitorTotals(since string) {
|
||||||
stmt, err := datastore.DB.Prepare(`
|
tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02")
|
||||||
SELECT
|
totals, err := datastore.VisitorCountPerDay(tomorrow, since)
|
||||||
COUNT(DISTINCT(pv.visitor_id)) AS count,
|
if err != nil {
|
||||||
DATE_FORMAT(pv.timestamp, "%Y-%m-%d") AS date_group
|
log.Fatal(err)
|
||||||
FROM pageviews pv
|
|
||||||
WHERE pv.timestamp > ?
|
|
||||||
GROUP BY date_group`)
|
|
||||||
checkError(err)
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
rows, err := stmt.Query(since)
|
|
||||||
checkError(err)
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
datastore.DB.Exec("START TRANSACTION")
|
|
||||||
for rows.Next() {
|
|
||||||
var t Total
|
|
||||||
err = rows.Scan(&t.Count, &t.Date)
|
|
||||||
checkError(err)
|
|
||||||
|
|
||||||
stmt, err := datastore.DB.Prepare(`INSERT INTO total_visitors(
|
|
||||||
count,
|
|
||||||
date
|
|
||||||
) VALUES( ?, ? ) ON DUPLICATE KEY UPDATE count = ?`)
|
|
||||||
checkError(err)
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
_, err = stmt.Exec(
|
|
||||||
t.Count,
|
|
||||||
t.Date,
|
|
||||||
t.Count,
|
|
||||||
)
|
|
||||||
checkError(err)
|
|
||||||
}
|
}
|
||||||
datastore.DB.Exec("COMMIT")
|
|
||||||
|
err = datastore.SaveVisitorTotals(totals)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package datastore
|
||||||
|
|
||||||
|
import "github.com/usefathom/fathom/pkg/models"
|
||||||
|
|
||||||
|
func BrowserCountPerDay(before string, after string) ([]*models.Total, error) {
|
||||||
|
var results []*models.Total
|
||||||
|
|
||||||
|
query := dbx.Rebind(`
|
||||||
|
SELECT
|
||||||
|
v.browser_name AS value,
|
||||||
|
COUNT(*) AS count,
|
||||||
|
COUNT(DISTINCT(pv.visitor_id)) AS count_unique,
|
||||||
|
DATE_FORMAT(pv.timestamp, "%Y-%m-%d") AS date_group
|
||||||
|
FROM pageviews pv
|
||||||
|
LEFT JOIN visitors v ON v.id = pv.visitor_id
|
||||||
|
WHERE pv.timestamp < ? AND pv.timestamp > ?
|
||||||
|
GROUP BY date_group, v.browser_name`)
|
||||||
|
|
||||||
|
err := dbx.Select(&results, query, before, after)
|
||||||
|
return results, err
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package datastore
|
||||||
|
|
||||||
|
import "github.com/usefathom/fathom/pkg/models"
|
||||||
|
|
||||||
|
func LanguageCountPerDay(before string, after string) ([]*models.Total, error) {
|
||||||
|
var results []*models.Total
|
||||||
|
|
||||||
|
query := dbx.Rebind(`
|
||||||
|
SELECT
|
||||||
|
v.browser_language AS value,
|
||||||
|
COUNT(*) AS count,
|
||||||
|
COUNT(DISTINCT(pv.visitor_id)) AS count_unique,
|
||||||
|
DATE_FORMAT(pv.timestamp, "%Y-%m-%d") AS date_group
|
||||||
|
FROM pageviews pv
|
||||||
|
LEFT JOIN visitors v ON v.id = pv.visitor_id
|
||||||
|
WHERE pv.timestamp < ? AND pv.timestamp > ?
|
||||||
|
GROUP BY date_group, v.browser_language`)
|
||||||
|
|
||||||
|
err := dbx.Select(&results, query, before, after)
|
||||||
|
return results, err
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package datastore
|
||||||
|
|
||||||
|
import "github.com/usefathom/fathom/pkg/models"
|
||||||
|
|
||||||
|
func ReferrerCountPerDay(before string, after string) ([]*models.Total, error) {
|
||||||
|
var results []*models.Total
|
||||||
|
|
||||||
|
query := dbx.Rebind(`
|
||||||
|
SELECT
|
||||||
|
pv.referrer_url AS value,
|
||||||
|
COUNT(*) AS count,
|
||||||
|
COUNT(DISTINCT(pv.visitor_id)) AS count_unique,
|
||||||
|
DATE_FORMAT(pv.timestamp, "%Y-%m-%d") AS date_group
|
||||||
|
FROM pageviews pv
|
||||||
|
WHERE pv.referrer_url IS NOT NULL
|
||||||
|
AND pv.referrer_url != ''
|
||||||
|
AND pv.timestamp < ? AND pv.timestamp > ?
|
||||||
|
GROUP BY date_group, pv.referrer_url`)
|
||||||
|
|
||||||
|
err := dbx.Select(&results, query, before, after)
|
||||||
|
return results, err
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package datastore
|
||||||
|
|
||||||
|
import "github.com/usefathom/fathom/pkg/models"
|
||||||
|
|
||||||
|
func ScreenCountPerDay(before string, after string) ([]*models.Total, error) {
|
||||||
|
var results []*models.Total
|
||||||
|
|
||||||
|
query := dbx.Rebind(`
|
||||||
|
SELECT
|
||||||
|
v.screen_resolution AS value,
|
||||||
|
COUNT(*) AS count,
|
||||||
|
COUNT(DISTINCT(pv.visitor_id)) AS count_unique,
|
||||||
|
DATE_FORMAT(pv.timestamp, "%Y-%m-%d") AS date_group
|
||||||
|
FROM pageviews pv
|
||||||
|
LEFT JOIN visitors v ON v.id = pv.visitor_id
|
||||||
|
WHERE pv.timestamp < ? AND pv.timestamp > ?
|
||||||
|
GROUP BY date_group, v.screen_resolution`)
|
||||||
|
|
||||||
|
err := dbx.Select(&results, query, before, after)
|
||||||
|
return results, err
|
||||||
|
}
|
|
@ -133,16 +133,12 @@ func Seed(n int) {
|
||||||
Timestamp: timestamp,
|
Timestamp: timestamp,
|
||||||
}
|
}
|
||||||
|
|
||||||
DB.Exec("START TRANSACTION")
|
|
||||||
|
|
||||||
// insert between 1-6 pageviews for this visitor
|
// insert between 1-6 pageviews for this visitor
|
||||||
for j := 0; j <= randInt(1, 6); j++ {
|
for j := 0; j <= randInt(1, 6); j++ {
|
||||||
page := pages[randInt(0, len(pages))]
|
page := pages[randInt(0, len(pages))]
|
||||||
pv.PageID = page.ID
|
pv.PageID = page.ID
|
||||||
SavePageview(&pv)
|
SavePageview(&pv)
|
||||||
}
|
}
|
||||||
|
|
||||||
DB.Exec("COMMIT")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package datastore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/usefathom/fathom/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SaveTotals(metric string, totals []*models.Total) error {
|
||||||
|
query := dbx.Rebind(fmt.Sprintf(`
|
||||||
|
INSERT INTO total_%s( value, count, count_unique, date)
|
||||||
|
VALUES( ?, ?, ?, ? ) ON DUPLICATE KEY UPDATE count = ?, count_unique = ?
|
||||||
|
`, metric))
|
||||||
|
|
||||||
|
tx, err := dbx.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(query)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
for _, t := range totals {
|
||||||
|
result, err := stmt.Exec(t.Value, t.Count, t.CountUnique, t.Date, t.Count, t.CountUnique)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.ID, _ = result.LastInsertId()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package datastore
|
||||||
|
|
||||||
|
// TotalUniqueBrowsers returns the total # of unique browsers between two given timestamps
|
||||||
|
func TotalUniqueBrowsers(before int64, after int64) (int, error) {
|
||||||
|
var total int
|
||||||
|
|
||||||
|
query := dbx.Rebind(`
|
||||||
|
SELECT
|
||||||
|
SUM(IFNULL(t.count_unique, 0))
|
||||||
|
FROM total_browser_names t
|
||||||
|
WHERE UNIX_TIMESTAMP(t.date) <= ? AND UNIX_TIMESTAMP(t.date) >= ?`)
|
||||||
|
err := dbx.Get(&total, query, before, after)
|
||||||
|
return total, err
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package datastore
|
||||||
|
|
||||||
|
// TotalUniqueLanguages returns the total # of unique browser languages between two given timestamps
|
||||||
|
func TotalUniqueLanguages(before int64, after int64) (int, error) {
|
||||||
|
var total int
|
||||||
|
|
||||||
|
query := dbx.Rebind(`
|
||||||
|
SELECT
|
||||||
|
IFNULL( SUM(t.count_unique), 0 )
|
||||||
|
FROM total_browser_languages t
|
||||||
|
WHERE UNIX_TIMESTAMP(t.date) <= ? AND UNIX_TIMESTAMP(t.date) >= ?`)
|
||||||
|
|
||||||
|
err := dbx.Get(&total, query, before, after)
|
||||||
|
return total, err
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package datastore
|
||||||
|
|
||||||
|
// TotalReferrers returns the total # of referrers between two given timestamps
|
||||||
|
func TotalReferrers(before int64, after int64) (int, error) {
|
||||||
|
var total int
|
||||||
|
|
||||||
|
query := dbx.Rebind(`
|
||||||
|
SELECT
|
||||||
|
IFNULL( SUM(t.count), 0 )
|
||||||
|
FROM total_referrers t
|
||||||
|
WHERE UNIX_TIMESTAMP(t.date) <= ? AND UNIX_TIMESTAMP(t.date) >= ?`)
|
||||||
|
|
||||||
|
err := dbx.Get(&total, query, before, after)
|
||||||
|
return total, err
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package datastore
|
||||||
|
|
||||||
|
// TotalUniqueScreens returns the total # of screens between two given timestamps
|
||||||
|
func TotalUniqueScreens(before int64, after int64) (int, error) {
|
||||||
|
var total int
|
||||||
|
|
||||||
|
query := dbx.Rebind(`
|
||||||
|
SELECT
|
||||||
|
IFNULL( SUM(t.count_unique), 0 )
|
||||||
|
FROM total_screens t
|
||||||
|
WHERE UNIX_TIMESTAMP(t.date) <= ? AND UNIX_TIMESTAMP(t.date) >= ?`)
|
||||||
|
err := dbx.Get(&total, query, before, after)
|
||||||
|
return total, err
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package datastore
|
||||||
|
|
||||||
|
import "github.com/usefathom/fathom/pkg/models"
|
||||||
|
|
||||||
|
// TotalVisitors returns the number of total visitors between the given timestamps
|
||||||
|
func TotalVisitors(before int64, after int64) (int, error) {
|
||||||
|
var total int
|
||||||
|
|
||||||
|
query := dbx.Rebind(`
|
||||||
|
SELECT IFNULL(SUM(t.count), 0)
|
||||||
|
FROM total_visitors t
|
||||||
|
WHERE UNIX_TIMESTAMP(t.date) <= ? AND UNIX_TIMESTAMP(t.date) >= ?`)
|
||||||
|
err := dbx.Get(&total, query, before, after)
|
||||||
|
return total, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TotalVisitorsPerDay returns a point slice containing visitor data per day
|
||||||
|
func TotalVisitorsPerDay(before int64, after int64) ([]*models.Point, error) {
|
||||||
|
var results []*models.Point
|
||||||
|
|
||||||
|
query := dbx.Rebind(`SELECT
|
||||||
|
SUM(IFNULL(t.count, 0)) AS value,
|
||||||
|
DATE_FORMAT(t.date, '%Y-%m-%d') AS label
|
||||||
|
FROM total_visitors t
|
||||||
|
WHERE UNIX_TIMESTAMP(t.date) <= ? AND UNIX_TIMESTAMP(t.date) >= ?
|
||||||
|
GROUP BY label`)
|
||||||
|
|
||||||
|
err := dbx.Select(&results, query, before, after)
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveVisitorTotals(totals []*models.Total) error {
|
||||||
|
tx, err := dbx.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
query := dbx.Rebind(`INSERT INTO total_visitors( count, date ) VALUES( ?, ? ) ON DUPLICATE KEY UPDATE count = ?`)
|
||||||
|
stmt, err := tx.Prepare(query)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t := range totals {
|
||||||
|
_, err = stmt.Exec(t.Count, t.Date, t.Count)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return err
|
||||||
|
}
|
|
@ -34,3 +34,30 @@ func SaveVisitor(v *models.Visitor) error {
|
||||||
v.ID, _ = result.LastInsertId()
|
v.ID, _ = result.LastInsertId()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RealtimeVisitors returns the total number of visitors in the last 3 minutes
|
||||||
|
// TODO: Query visitors table instead, using a last_seen column.
|
||||||
|
func RealtimeVisitors() (int, error) {
|
||||||
|
var result int
|
||||||
|
query := dbx.Rebind(`
|
||||||
|
SELECT COUNT(DISTINCT(pv.visitor_id))
|
||||||
|
FROM pageviews pv
|
||||||
|
WHERE pv.timestamp >= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 3 HOUR_MINUTE) AND pv.timestamp <= CURRENT_TIMESTAMP`)
|
||||||
|
err := dbx.Get(&result, query)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func VisitorCountPerDay(before string, after string) ([]*models.Total, error) {
|
||||||
|
var results []*models.Total
|
||||||
|
|
||||||
|
query := dbx.Rebind(`
|
||||||
|
SELECT
|
||||||
|
COUNT(DISTINCT(pv.visitor_id)) AS count,
|
||||||
|
DATE_FORMAT(pv.timestamp, "%Y-%m-%d") AS date_group
|
||||||
|
FROM pageviews pv
|
||||||
|
WHERE pv.timestamp < ? AND pv.timestamp > ?
|
||||||
|
GROUP BY date_group`)
|
||||||
|
|
||||||
|
err := dbx.Select(&results, query, before, after)
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue