diff --git a/ana.go b/ana.go index 97fe339..323a9a9 100644 --- a/ana.go +++ b/ana.go @@ -34,6 +34,8 @@ func main() { r.Handle("/api/pageviews/count/day", api.Authorize(api.GetPageviewsDayCountHandler)).Methods("GET") r.Handle("/api/pageviews", api.Authorize(api.GetPageviewsHandler)).Methods("GET") r.Handle("/api/languages", api.Authorize(api.GetLanguagesHandler)).Methods("GET") + r.Handle("/api/screen-resolutions", api.Authorize(api.GetScreenResolutionsHandler)).Methods("GET") + r.Handle("/api/countries", api.Authorize(api.GetCountriesHandler)).Methods("GET") r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("./static/")))) r.Handle("/", http.FileServer(http.Dir("./views/"))) diff --git a/api/api.go b/api/api.go index ab480ec..ac66de1 100644 --- a/api/api.go +++ b/api/api.go @@ -3,11 +3,14 @@ package api import ( "log" "time" + "strconv" + "net/http" ) type Datapoint struct { Count int Label string + Percentage float32 `json:",omitempty"` } var defaultPeriod = 7 @@ -42,3 +45,11 @@ func fillDatapoints(days int, points []Datapoint) []Datapoint { return newPoints } + +func getRequestedPeriod(r *http.Request) int { + period, err := strconv.Atoi(r.URL.Query().Get("period")) + if err != nil || period == 0 { + period = defaultPeriod + } + return period +} diff --git a/api/countries.go b/api/countries.go new file mode 100644 index 0000000..684482d --- /dev/null +++ b/api/countries.go @@ -0,0 +1,51 @@ +package api + +import ( + "net/http" + "github.com/dannyvankooten/ana/core" + "encoding/json" +) + +// URL: /api/countries +var GetCountriesHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + period := getRequestedPeriod(r) + + // get total + stmt, err := core.DB.Prepare(` + SELECT + COUNT(DISTINCT(ip_address)) + FROM visits + WHERE timestamp >= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL ? DAY)`) + checkError(err) + defer stmt.Close() + var total float32 + stmt.QueryRow(period).Scan(&total) + + // get rows + stmt, err = core.DB.Prepare(` + SELECT + country, + COUNT(DISTINCT(ip_address)) AS count + FROM visits + WHERE timestamp >= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL ? DAY) AND country IS NOT NULL + GROUP BY country + ORDER BY count DESC`) + checkError(err) + defer stmt.Close() + rows, err := stmt.Query(period) + checkError(err) + defer rows.Close() + + results := make([]Datapoint, 0) + for rows.Next() { + var d Datapoint + err = rows.Scan(&d.Label, &d.Count); + checkError(err) + + d.Percentage = float32(d.Count) / total * 100 + results = append(results, d) + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(results) +}) diff --git a/api/languages.go b/api/languages.go index 12c41e4..4f5185c 100644 --- a/api/languages.go +++ b/api/languages.go @@ -4,12 +4,23 @@ import ( "net/http" "github.com/dannyvankooten/ana/core" "encoding/json" - "strconv" ) // URL: /api/languages var GetLanguagesHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + period := getRequestedPeriod(r) + stmt, err := core.DB.Prepare(` + SELECT + COUNT(DISTINCT(ip_address)) + FROM visits + WHERE timestamp >= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL ? DAY)`) + checkError(err) + defer stmt.Close() + var total float32 + stmt.QueryRow(period).Scan(&total) + + stmt, err = core.DB.Prepare(` SELECT browser_language, COUNT(DISTINCT(ip_address)) AS count @@ -20,46 +31,21 @@ var GetLanguagesHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.R checkError(err) defer stmt.Close() - - stmt2, err := core.DB.Prepare(` - SELECT - COUNT(DISTINCT(ip_address)) - FROM visits - WHERE timestamp >= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL ? DAY)`) - checkError(err) - defer stmt2.Close() - - period, err := strconv.Atoi(r.URL.Query().Get("period")) - if err != nil || period == 0 { - period = defaultPeriod - } - - var total float32 - stmt2.QueryRow(period).Scan(&total) - rows, err := stmt.Query(period) checkError(err) - - type LanguageData struct { - Count int - Language string - Percentage float32 - } - - results := make([]LanguageData, 0) defer rows.Close() + + results := make([]Datapoint, 0) + for rows.Next() { - var l LanguageData - err = rows.Scan(&l.Language, &l.Count); + var d Datapoint + err = rows.Scan(&d.Label, &d.Count); checkError(err) - l.Percentage = float32(l.Count) / total * 100 - results = append(results, l) + d.Percentage = float32(d.Count) / total * 100 + results = append(results, d) } - err = rows.Err(); - checkError(err) - w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(results) }) diff --git a/api/pageviews.go b/api/pageviews.go index ba2b370..48ac5fc 100644 --- a/api/pageviews.go +++ b/api/pageviews.go @@ -5,11 +5,12 @@ import ( "github.com/dannyvankooten/ana/models" "github.com/dannyvankooten/ana/core" "encoding/json" - "strconv" ) // URL: /api/pageviews var GetPageviewsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + period := getRequestedPeriod(r) + stmt, err := core.DB.Prepare(`SELECT path, COUNT(ip_address) AS pageviews, @@ -21,16 +22,11 @@ var GetPageviewsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.R checkError(err) defer stmt.Close() - period, err := strconv.Atoi(r.URL.Query().Get("period")) - if err != nil || period == 0 { - period = defaultPeriod - } - rows, err := stmt.Query(period) checkError(err) + defer rows.Close() results := make([]models.Pageview, 0) - defer rows.Close() for rows.Next() { var p models.Pageview err = rows.Scan(&p.Path, &p.Count, &p.CountUnique); @@ -48,6 +44,8 @@ var GetPageviewsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.R // URL: /api/pageviews/count/day var GetPageviewsDayCountHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + period := getRequestedPeriod(r) + stmt, err := core.DB.Prepare(`SELECT COUNT(*) AS count, DATE_FORMAT(timestamp, '%Y-%m-%d') AS date_group FROM visits @@ -56,16 +54,11 @@ var GetPageviewsDayCountHandler = http.HandlerFunc(func(w http.ResponseWriter, r checkError(err) defer stmt.Close() - period, err := strconv.Atoi(r.URL.Query().Get("period")) - if err != nil || period == 0 { - period = 7 - } - rows, err := stmt.Query(period) checkError(err) + defer rows.Close() results := make([]Datapoint, 0) - defer rows.Close() for rows.Next() { v := Datapoint{} err = rows.Scan(&v.Count, &v.Label); @@ -75,9 +68,6 @@ var GetPageviewsDayCountHandler = http.HandlerFunc(func(w http.ResponseWriter, r results = fillDatapoints(period, results) - err = rows.Err(); - checkError(err) - w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(results) }) diff --git a/api/screen-resolutions.go b/api/screen-resolutions.go new file mode 100644 index 0000000..54d2194 --- /dev/null +++ b/api/screen-resolutions.go @@ -0,0 +1,51 @@ +package api + +import ( + "net/http" + "github.com/dannyvankooten/ana/core" + "encoding/json" +) + +// URL: /api/screen-resolutions +var GetScreenResolutionsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + period := getRequestedPeriod(r) + + // get total + stmt, err := core.DB.Prepare(` + SELECT + COUNT(DISTINCT(ip_address)) + FROM visits + WHERE timestamp >= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL ? DAY)`) + checkError(err) + defer stmt.Close() + var total float32 + stmt.QueryRow(period).Scan(&total) + + // get rows + stmt, err = core.DB.Prepare(` + SELECT + screen_resolution, + COUNT(DISTINCT(ip_address)) AS count + FROM visits + WHERE timestamp >= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL ? DAY) + GROUP BY screen_resolution + ORDER BY count DESC`) + checkError(err) + defer stmt.Close() + rows, err := stmt.Query(period) + checkError(err) + defer rows.Close() + + results := make([]Datapoint, 0) + for rows.Next() { + var d Datapoint + err = rows.Scan(&d.Label, &d.Count); + checkError(err) + + d.Percentage = float32(d.Count) / total * 100 + results = append(results, d) + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(results) +})