summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Manuel <nilslice@gmail.com>2016-11-03 23:15:47 -0700
committerSteve Manuel <nilslice@gmail.com>2016-11-03 23:15:47 -0700
commita3ced7a753ef5a2aed0a480985c7b69abf244ac2 (patch)
tree8f9684b3eaba469d833d7ca2e97dfa7a294ca680
parentc9aa995b43d2f24377e8a9ffb07983a62df15dea (diff)
testing new api request analytics in dashboard
-rw-r--r--system/admin/admin.go36
-rw-r--r--system/admin/handlers.go2
-rw-r--r--system/api/analytics/init.go123
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
}