2016-12-11 10:53:03 +00:00
|
|
|
package count
|
2016-12-11 09:58:58 +00:00
|
|
|
|
2016-12-11 13:50:01 +00:00
|
|
|
import (
|
|
|
|
"database/sql"
|
|
|
|
"log"
|
|
|
|
"time"
|
2016-12-23 14:03:11 +00:00
|
|
|
|
|
|
|
"github.com/dannyvankooten/ana/db"
|
2016-12-24 13:14:25 +00:00
|
|
|
"github.com/dannyvankooten/ana/options"
|
2016-12-11 09:58:58 +00:00
|
|
|
)
|
|
|
|
|
2016-12-24 11:07:33 +00:00
|
|
|
// Total represents a daily aggregated total for a metric
|
|
|
|
type Total struct {
|
|
|
|
ID int64
|
|
|
|
PageID int64
|
|
|
|
Value string
|
|
|
|
Count int64
|
|
|
|
CountUnique int64
|
|
|
|
Date string
|
2016-12-11 09:58:58 +00:00
|
|
|
}
|
|
|
|
|
2016-12-23 14:03:11 +00:00
|
|
|
// Point represents a data point, will always have a Label and Value
|
2016-12-11 11:52:10 +00:00
|
|
|
type Point struct {
|
2016-12-11 13:50:01 +00:00
|
|
|
Label string
|
|
|
|
Value int
|
|
|
|
PercentageValue float64
|
2016-12-11 11:52:10 +00:00
|
|
|
}
|
|
|
|
|
2016-12-25 16:24:53 +00:00
|
|
|
func getLastArchivedDate() string {
|
2016-12-24 13:14:25 +00:00
|
|
|
value := options.Get("last_archived")
|
2016-12-25 16:24:53 +00:00
|
|
|
return value
|
2016-12-24 13:14:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Archive aggregates data into daily totals
|
|
|
|
func Archive() {
|
2016-12-25 16:24:53 +00:00
|
|
|
lastArchived := getLastArchivedDate()
|
2016-12-24 13:14:25 +00:00
|
|
|
|
|
|
|
CreateVisitorTotals(lastArchived)
|
|
|
|
CreatePageviewTotals(lastArchived)
|
|
|
|
CreateScreenTotals(lastArchived)
|
|
|
|
CreateLanguageTotals(lastArchived)
|
|
|
|
CreateBrowserTotals(lastArchived)
|
|
|
|
CreateReferrerTotals(lastArchived)
|
|
|
|
|
2016-12-25 16:24:53 +00:00
|
|
|
err := options.Set("last_archived", time.Now().Format("2006-01-02"))
|
2016-12-24 13:14:25 +00:00
|
|
|
checkError(err)
|
|
|
|
}
|
|
|
|
|
2016-12-24 11:07:33 +00:00
|
|
|
// Save the Total in the given database connection + table
|
|
|
|
func (t *Total) Save(Conn *sql.DB, table string) error {
|
|
|
|
stmt, err := db.Conn.Prepare(`INSERT INTO ` + table + `(
|
2016-12-11 09:58:58 +00:00
|
|
|
value,
|
|
|
|
count,
|
2016-12-24 11:07:33 +00:00
|
|
|
count_unique,
|
2016-12-11 09:58:58 +00:00
|
|
|
date
|
2016-12-25 16:24:53 +00:00
|
|
|
) VALUES( ?, ?, ?, ? ) ON DUPLICATE KEY UPDATE count = ?, count_unique = ?`)
|
2016-12-11 13:50:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer stmt.Close()
|
|
|
|
|
|
|
|
result, err := stmt.Exec(
|
2016-12-24 11:07:33 +00:00
|
|
|
t.Value,
|
|
|
|
t.Count,
|
|
|
|
t.CountUnique,
|
|
|
|
t.Date,
|
2016-12-25 16:24:53 +00:00
|
|
|
t.Count,
|
|
|
|
t.CountUnique,
|
2016-12-11 13:50:01 +00:00
|
|
|
)
|
2016-12-24 11:07:33 +00:00
|
|
|
t.ID, _ = result.LastInsertId()
|
2016-12-11 13:50:01 +00:00
|
|
|
|
|
|
|
return err
|
2016-12-11 09:58:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func checkError(err error) {
|
2016-12-11 13:50:01 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2016-12-11 09:58:58 +00:00
|
|
|
}
|
2016-12-11 11:52:10 +00:00
|
|
|
|
2016-12-24 13:57:37 +00:00
|
|
|
func newPointSlice(rows *sql.Rows) []Point {
|
2016-12-23 15:52:08 +00:00
|
|
|
results := make([]Point, 0)
|
|
|
|
|
2016-12-24 13:57:37 +00:00
|
|
|
// append point slices
|
2016-12-11 13:50:01 +00:00
|
|
|
for rows.Next() {
|
|
|
|
var d Point
|
|
|
|
err := rows.Scan(&d.Label, &d.Value)
|
|
|
|
checkError(err)
|
|
|
|
results = append(results, d)
|
2016-12-24 13:57:37 +00:00
|
|
|
}
|
|
|
|
|
2016-12-25 16:47:11 +00:00
|
|
|
return results
|
|
|
|
}
|
|
|
|
|
|
|
|
func calculatePointPercentages(points []Point, total int) []Point {
|
2016-12-24 13:57:37 +00:00
|
|
|
// calculate percentage values for each point
|
2016-12-25 16:47:11 +00:00
|
|
|
for i, d := range points {
|
|
|
|
points[i].PercentageValue = float64(d.Value) / float64(total) * 100
|
2016-12-11 13:50:01 +00:00
|
|
|
}
|
2016-12-25 16:47:11 +00:00
|
|
|
|
|
|
|
return points
|
2016-12-11 11:52:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func fill(start int64, end int64, points []Point) []Point {
|
2016-12-11 13:50:01 +00:00
|
|
|
// be smart about received timestamps
|
|
|
|
if start > end {
|
|
|
|
tmp := end
|
|
|
|
end = start
|
|
|
|
start = tmp
|
|
|
|
}
|
|
|
|
|
|
|
|
startTime := time.Unix(start, 0)
|
|
|
|
endTime := time.Unix(end, 0)
|
2016-12-23 14:03:11 +00:00
|
|
|
var newPoints []Point
|
2016-12-11 13:50:01 +00:00
|
|
|
step := time.Hour * 24
|
|
|
|
|
|
|
|
for startTime.Before(endTime) || startTime.Equal(endTime) {
|
|
|
|
point := Point{
|
|
|
|
Value: 0,
|
|
|
|
Label: startTime.Format("2006-01-02"),
|
|
|
|
}
|
|
|
|
|
|
|
|
for j, p := range points {
|
|
|
|
if p.Label == point.Label || p.Label == startTime.Format("2006-01") {
|
|
|
|
point.Value = p.Value
|
|
|
|
points[j] = points[len(points)-1]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
newPoints = append(newPoints, point)
|
|
|
|
startTime = startTime.Add(step)
|
|
|
|
}
|
|
|
|
|
|
|
|
return newPoints
|
2016-12-11 11:52:10 +00:00
|
|
|
}
|
2016-12-24 11:07:33 +00:00
|
|
|
|
2016-12-25 16:24:53 +00:00
|
|
|
func queryTotalRows(sql string, lastArchived string) *sql.Rows {
|
2016-12-24 11:07:33 +00:00
|
|
|
stmt, err := db.Conn.Prepare(sql)
|
|
|
|
checkError(err)
|
|
|
|
defer stmt.Close()
|
|
|
|
|
2016-12-24 13:14:25 +00:00
|
|
|
rows, err := stmt.Query(lastArchived)
|
2016-12-24 11:07:33 +00:00
|
|
|
checkError(err)
|
|
|
|
return rows
|
|
|
|
}
|
|
|
|
|
|
|
|
func processTotalRows(rows *sql.Rows, table string) {
|
|
|
|
db.Conn.Exec("START TRANSACTION")
|
|
|
|
for rows.Next() {
|
|
|
|
var t Total
|
|
|
|
err := rows.Scan(&t.Value, &t.Count, &t.CountUnique, &t.Date)
|
|
|
|
checkError(err)
|
|
|
|
t.Save(db.Conn, table)
|
|
|
|
}
|
|
|
|
db.Conn.Exec("COMMIT")
|
|
|
|
|
|
|
|
rows.Close()
|
|
|
|
}
|