diff --git a/pkg/api/visitors.go b/pkg/api/visitors.go index 8bea94f..848cf35 100644 --- a/pkg/api/visitors.go +++ b/pkg/api/visitors.go @@ -3,25 +3,36 @@ package api import ( "net/http" - "github.com/usefathom/fathom/pkg/count" + "github.com/usefathom/fathom/pkg/datastore" ) // URL: /api/visitors/count var GetVisitorsCountHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error { 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}) }) // URL: /api/visitors/count/realtime 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}) }) // URL: /api/visitors/count/group/:period var GetVisitorsPeriodCountHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error { 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}) }) diff --git a/pkg/count/browsers.go b/pkg/count/browsers.go index 1cbe699..87d8471 100644 --- a/pkg/count/browsers.go +++ b/pkg/count/browsers.go @@ -1,27 +1,12 @@ package count import ( + "log" + "time" + "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 func Browsers(before int64, after int64, limit int) []Point { stmt, err := datastore.DB.Prepare(` @@ -40,7 +25,9 @@ func Browsers(before int64, after int64, limit int) []Point { checkError(err) points := newPointSlice(rows) - total := TotalUniqueBrowsers(before, after) + total, err := datastore.TotalUniqueBrowsers(before, after) + checkError(err) + points = calculatePointPercentages(points, total) return points @@ -48,16 +35,14 @@ func Browsers(before int64, after int64, limit int) []Point { // CreateBrowserTotals aggregates screen data into daily totals func CreateBrowserTotals(since string) { - rows := queryTotalRows(` - SELECT - v.browser_name, - 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 > ? - GROUP BY date_group, v.browser_name`, since) + tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02") + totals, err := datastore.BrowserCountPerDay(tomorrow, since) + if err != nil { + log.Fatal(err) + } - processTotalRows(rows, "total_browser_names") + err = datastore.SaveTotals("browser_names", totals) + if err != nil { + log.Fatal(err) + } } diff --git a/pkg/count/count.go b/pkg/count/count.go index 1b47ab3..9f99af2 100644 --- a/pkg/count/count.go +++ b/pkg/count/count.go @@ -2,22 +2,13 @@ package count import ( "database/sql" - log "github.com/sirupsen/logrus" "time" + log "github.com/sirupsen/logrus" + "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 type Point struct { Label string @@ -27,6 +18,10 @@ type Point struct { func getLastArchivedDate() string { value, _ := datastore.GetOption("last_archived") + if value == "" { + return time.Now().AddDate(-1, 0, 0).Format("2006-01-02") + } + return value } @@ -47,32 +42,6 @@ func Archive() { 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) { if err != nil { log.Fatal(err) @@ -101,26 +70,3 @@ func calculatePointPercentages(points []Point, total int) []Point { 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() -} diff --git a/pkg/count/languages.go b/pkg/count/languages.go index fe643e2..ab2741a 100644 --- a/pkg/count/languages.go +++ b/pkg/count/languages.go @@ -1,30 +1,14 @@ package count import ( + "log" + "time" + "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 func Languages(before int64, after int64, limit int) []Point { - // TODO: Calculate total instead of requiring it as a parameter. stmt, err := datastore.DB.Prepare(` SELECT t.value, @@ -41,7 +25,9 @@ func Languages(before int64, after int64, limit int) []Point { checkError(err) points := newPointSlice(rows) - total := TotalUniqueLanguages(before, after) + total, err := datastore.TotalUniqueLanguages(before, after) + checkError(err) + points = calculatePointPercentages(points, total) return points @@ -49,16 +35,14 @@ func Languages(before int64, after int64, limit int) []Point { // CreateLanguageTotals aggregates screen data into daily totals func CreateLanguageTotals(since string) { - rows := queryTotalRows(` - SELECT - v.browser_language, - 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 > ? - GROUP BY date_group, v.browser_language`, since) + tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02") + totals, err := datastore.LanguageCountPerDay(tomorrow, since) + if err != nil { + log.Fatal(err) + } - processTotalRows(rows, "total_browser_languages") + err = datastore.SaveTotals("browser_languages", totals) + if err != nil { + log.Fatal(err) + } } diff --git a/pkg/count/pageviews.go b/pkg/count/pageviews.go index 68c1960..a9a85b6 100644 --- a/pkg/count/pageviews.go +++ b/pkg/count/pageviews.go @@ -1,14 +1,15 @@ package count import ( - "github.com/usefathom/fathom/pkg/datastore" "log" "time" + + "github.com/usefathom/fathom/pkg/datastore" ) // CreatePageviewTotals aggregates pageview data for each page into daily totals 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) if err != nil { log.Fatal(err) diff --git a/pkg/count/referrers.go b/pkg/count/referrers.go index c1dde37..233a3f9 100644 --- a/pkg/count/referrers.go +++ b/pkg/count/referrers.go @@ -1,27 +1,12 @@ package count import ( + "log" + "time" + "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 func Referrers(before int64, after int64, limit int) []Point { stmt, err := datastore.DB.Prepare(` @@ -40,7 +25,9 @@ func Referrers(before int64, after int64, limit int) []Point { checkError(err) points := newPointSlice(rows) - total := TotalReferrers(before, after) + total, err := datastore.TotalReferrers(before, after) + checkError(err) + points = calculatePointPercentages(points, total) return points @@ -48,17 +35,14 @@ func Referrers(before int64, after int64, limit int) []Point { // CreateReferrerTotals aggregates screen data into daily totals func CreateReferrerTotals(since string) { - rows := queryTotalRows(` - SELECT - pv.referrer_url, - 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 > ? - GROUP BY date_group, pv.referrer_url`, since) + tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02") + totals, err := datastore.ReferrerCountPerDay(tomorrow, since) + if err != nil { + log.Fatal(err) + } - processTotalRows(rows, "total_referrers") + err = datastore.SaveTotals("referrers", totals) + if err != nil { + log.Fatal(err) + } } diff --git a/pkg/count/screens.go b/pkg/count/screens.go index a0efca2..cf6a21b 100644 --- a/pkg/count/screens.go +++ b/pkg/count/screens.go @@ -1,27 +1,12 @@ package count import ( + "log" + "time" + "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 func Screens(before int64, after int64, limit int) []Point { stmt, err := datastore.DB.Prepare(` @@ -40,7 +25,9 @@ func Screens(before int64, after int64, limit int) []Point { checkError(err) points := newPointSlice(rows) - total := TotalUniqueScreens(before, after) + total, err := datastore.TotalUniqueScreens(before, after) + checkError(err) + points = calculatePointPercentages(points, total) return points @@ -48,16 +35,14 @@ func Screens(before int64, after int64, limit int) []Point { // CreateScreenTotals aggregates screen data into daily totals func CreateScreenTotals(since string) { - rows := queryTotalRows(` - SELECT - v.screen_resolution, - 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 > ? - GROUP BY date_group, v.screen_resolution`, since) + tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02") + totals, err := datastore.ScreenCountPerDay(tomorrow, since) + if err != nil { + log.Fatal(err) + } - processTotalRows(rows, "total_screens") + err = datastore.SaveTotals("screens", totals) + if err != nil { + log.Fatal(err) + } } diff --git a/pkg/count/visitors.go b/pkg/count/visitors.go index ed2b7dd..ac6edd0 100644 --- a/pkg/count/visitors.go +++ b/pkg/count/visitors.go @@ -1,95 +1,23 @@ package count import ( + "log" + "time" + "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 func CreateVisitorTotals(since string) { - stmt, err := datastore.DB.Prepare(` - SELECT - COUNT(DISTINCT(pv.visitor_id)) AS count, - DATE_FORMAT(pv.timestamp, "%Y-%m-%d") AS date_group - 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) + tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02") + totals, err := datastore.VisitorCountPerDay(tomorrow, since) + if err != nil { + log.Fatal(err) } - datastore.DB.Exec("COMMIT") + + err = datastore.SaveVisitorTotals(totals) + if err != nil { + log.Fatal(err) + } + } diff --git a/pkg/datastore/browsers.go b/pkg/datastore/browsers.go new file mode 100644 index 0000000..f80010a --- /dev/null +++ b/pkg/datastore/browsers.go @@ -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 +} diff --git a/pkg/datastore/languages.go b/pkg/datastore/languages.go new file mode 100644 index 0000000..e68b934 --- /dev/null +++ b/pkg/datastore/languages.go @@ -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 +} diff --git a/pkg/datastore/referrers.go b/pkg/datastore/referrers.go new file mode 100644 index 0000000..e8a91f2 --- /dev/null +++ b/pkg/datastore/referrers.go @@ -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 +} diff --git a/pkg/datastore/screens.go b/pkg/datastore/screens.go new file mode 100644 index 0000000..84f1e0b --- /dev/null +++ b/pkg/datastore/screens.go @@ -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 +} diff --git a/pkg/datastore/seed.go b/pkg/datastore/seed.go index 6ad1d20..ae48af9 100644 --- a/pkg/datastore/seed.go +++ b/pkg/datastore/seed.go @@ -133,16 +133,12 @@ func Seed(n int) { Timestamp: timestamp, } - DB.Exec("START TRANSACTION") - // insert between 1-6 pageviews for this visitor for j := 0; j <= randInt(1, 6); j++ { page := pages[randInt(0, len(pages))] pv.PageID = page.ID SavePageview(&pv) } - - DB.Exec("COMMIT") } } diff --git a/pkg/datastore/total.go b/pkg/datastore/total.go new file mode 100644 index 0000000..0dcafaf --- /dev/null +++ b/pkg/datastore/total.go @@ -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 +} diff --git a/pkg/datastore/total_browsers.go b/pkg/datastore/total_browsers.go new file mode 100644 index 0000000..c83f8f3 --- /dev/null +++ b/pkg/datastore/total_browsers.go @@ -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 +} diff --git a/pkg/datastore/total_languages.go b/pkg/datastore/total_languages.go new file mode 100644 index 0000000..a2aa2bf --- /dev/null +++ b/pkg/datastore/total_languages.go @@ -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 +} diff --git a/pkg/datastore/total_referrers.go b/pkg/datastore/total_referrers.go new file mode 100644 index 0000000..75600fe --- /dev/null +++ b/pkg/datastore/total_referrers.go @@ -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 +} diff --git a/pkg/datastore/total_screens.go b/pkg/datastore/total_screens.go new file mode 100644 index 0000000..9a8014a --- /dev/null +++ b/pkg/datastore/total_screens.go @@ -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 +} diff --git a/pkg/datastore/total_visitors.go b/pkg/datastore/total_visitors.go new file mode 100644 index 0000000..1129259 --- /dev/null +++ b/pkg/datastore/total_visitors.go @@ -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 +} diff --git a/pkg/datastore/visitors.go b/pkg/datastore/visitors.go index 9377b0c..b6e29d9 100644 --- a/pkg/datastore/visitors.go +++ b/pkg/datastore/visitors.go @@ -34,3 +34,30 @@ func SaveVisitor(v *models.Visitor) error { v.ID, _ = result.LastInsertId() 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 +}