diff options
author | Steve Manuel <nilslice@gmail.com> | 2016-11-03 23:15:47 -0700 |
---|---|---|
committer | Steve Manuel <nilslice@gmail.com> | 2016-11-03 23:15:47 -0700 |
commit | a3ced7a753ef5a2aed0a480985c7b69abf244ac2 (patch) | |
tree | 8f9684b3eaba469d833d7ca2e97dfa7a294ca680 | |
parent | c9aa995b43d2f24377e8a9ffb07983a62df15dea (diff) |
testing new api request analytics in dashboard
-rw-r--r-- | system/admin/admin.go | 36 | ||||
-rw-r--r-- | system/admin/handlers.go | 2 | ||||
-rw-r--r-- | system/api/analytics/init.go | 123 |
3 files changed, 152 insertions, 9 deletions
diff --git a/system/admin/admin.go b/system/admin/admin.go index 0979d11..0e690d5 100644 --- a/system/admin/admin.go +++ b/system/admin/admin.go @@ -10,6 +10,7 @@ import ( "github.com/bosssauce/ponzu/content" "github.com/bosssauce/ponzu/system/admin/user" + "github.com/bosssauce/ponzu/system/api/analytics" "github.com/bosssauce/ponzu/system/db" ) @@ -382,19 +383,21 @@ var analyticsHTML = ` var chart = new Chart(target, { type: 'bar', data: { - labels: ["10/28", "10/29", "10/30", "10/31", "11/1", "11/2", "11/3"], + labels: {{ .dates }}, datasets: [{ type: 'line', - label: 'Unique Requests', - data: [4435, 7231, 3555, 19121, 10876, 5009, 3564], + label: '{{ .unique.label }}', + // data: [4435, 7231, 3555, 19121, 10876, 5009, 3564], + data: {{ .unique.data }}, backgroundColor: 'rgba(76, 175, 80, 0.2)', borderColor: 'rgba(76, 175, 80, 1)', borderWidth: 1 }, { type: 'bar', - label: 'Total Requests', - data: [12332, 19333, 13545, 51776, 22334, 13334, 9089], + label: '{{ .total.label }}', + // data: [12332, 19333, 13545, 51776, 22334, 13334, 9089], + data: {{ .total.data }}, backgroundColor: 'rgba(33, 150, 243, 0.2)', borderColor: 'rgba(33, 150, 243, 1)', borderWidth: 1 @@ -416,6 +419,29 @@ var analyticsHTML = ` </div> ` +// Dashboard returns the admin view with analytics dashboard +func Dashboard() ([]byte, error) { + buf := &bytes.Buffer{} + + data, err := analytics.Week() + if err != nil { + return nil, err + } + + tmpl := template.Must(template.New("analytics").Parse(analyticsHTML)) + err = tmpl.Execute(buf, data) + if err != nil { + return nil, err + } + + view, err := Admin(buf.Bytes()) + if err != nil { + return nil, err + } + + return view, nil +} + var err400HTML = ` <div class="error-page e400 col s6"> <div class="card"> diff --git a/system/admin/handlers.go b/system/admin/handlers.go index 1291bbb..f9a9486 100644 --- a/system/admin/handlers.go +++ b/system/admin/handlers.go @@ -25,7 +25,7 @@ import ( ) func adminHandler(res http.ResponseWriter, req *http.Request) { - view, err := Admin([]byte(analyticsHTML)) + view, err := Dashboard() if err != nil { log.Println(err) res.WriteHeader(http.StatusInternalServerError) diff --git a/system/api/analytics/init.go b/system/api/analytics/init.go index 79cace0..e8416ea 100644 --- a/system/api/analytics/init.go +++ b/system/api/analytics/init.go @@ -11,6 +11,8 @@ import ( "runtime" + "encoding/json" + "github.com/boltdb/bolt" ) @@ -106,8 +108,123 @@ func serve() { } } -// Week returns the []byte of javascript needed to chart a week of data by day -func Week() { - // get request analytics for today and the 6 days preceeding +// Week returns the map containing decoded javascript needed to chart a week of data by day +func Week() (map[string]interface{}, error) { + // set thresholds for today and the 6 days preceeding + times := [7]time.Time{} + now := time.Now() + today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC) + + for i := range times { + // subtract 24 * i hours to make days prior + dur := time.Duration(24 * i * -1) + day := today.Add(time.Hour * dur) + + // day threshold is [...n-1-i, n-1, n] + times[len(times)-1-i] = day + } + + // get api request analytics from db + var requests = []apiRequest{} + err := store.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte("requests")) + + err := b.ForEach(func(k, v []byte) error { + var r apiRequest + err := json.Unmarshal(v, r) + if err != nil { + log.Println("Error decoding json from analytics db:", err) + return nil + } + + requests = append(requests, r) + + return nil + }) + if err != nil { + return err + } + + return nil + }) + if err != nil { + return nil, err + } + + dates := [7]string{} + ips := [7]map[string]struct{}{} + total := [7]int{} + unique := [7]int{} + +CHECK_REQUEST: + for i := range requests { + ts := time.Unix(requests[i].Timestamp/1000, 0) + + for j := range times { + // format times[j] (time.Time) into a MM/DD format for dates + dates[j] = times[j].Format("_1/_2") + + // if on today, there will be no next iteration to set values for + // day prior so all valid requests belong to today + if j == len(times) { + if ts.After(times[j]) || ts.Equal(times[j]) { + // do all record keeping + total[j]++ + + if _, ok := ips[j][requests[i].RemoteAddr]; !ok { + unique[j-1]++ + ips[j][requests[i].RemoteAddr] = struct{}{} + } + } + } + + if ts.Equal(times[j]) { + // increment total count for current time threshold (day) + total[j]++ + + // if no IP found for current threshold, increment unique and record IP + if _, ok := ips[j][requests[i].RemoteAddr]; !ok { + unique[j]++ + ips[j][requests[i].RemoteAddr] = struct{}{} + } + } + + if ts.Before(times[j]) { + // check if older than earliest threshold + if j == 0 { + continue CHECK_REQUEST + } + + // increment total count for previous time threshold (day) + total[j-1]++ + + // if no IP found for day prior, increment unique and record IP + if _, ok := ips[j-1][requests[i].RemoteAddr]; !ok { + unique[j-1]++ + ips[j-1][requests[i].RemoteAddr] = struct{}{} + } + } + } + } + + jsUnique, err := json.Marshal(unique) + if err != nil { + return nil, err + } + + jsTotal, err := json.Marshal(total) + if err != nil { + return nil, err + } + + jsDates, err := json.Marshal(dates) + if err != nil { + return nil, err + } + return map[string]interface{}{ + "dates": jsDates, + "unique": jsUnique, + "total": jsTotal, + }, nil } |