summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormatthieu <matthieu.cneude@gmail.com>2020-04-05 19:16:45 +0200
committermatthieu <matthieu.cneude@gmail.com>2020-04-05 19:16:45 +0200
commit5c8cd384bed4762c13895f26b23438135e41e369 (patch)
tree2421f3cf1e1e93e7e2685a56097b2c957a1bc596
parent361b27597c49cd557239aacdb4fd47333e6f49af (diff)
Add ga.bar_devices and improve code of stacked bar
-rw-r--r--internal/ga_widget.go68
-rw-r--r--internal/platform/ga.go48
-rw-r--r--internal/platform/ga_test.go51
-rw-r--r--internal/platform/termui.go6
-rw-r--r--internal/platform/testdata/fixtures/ga_bar_devices.json173
-rw-r--r--internal/tui.go4
6 files changed, 287 insertions, 63 deletions
diff --git a/internal/ga_widget.go b/internal/ga_widget.go
index 04dd82c..2c257d8 100644
--- a/internal/ga_widget.go
+++ b/internal/ga_widget.go
@@ -21,7 +21,8 @@ const (
gaBarReturning = "ga.bar_returning"
gaBarNewReturning = "ga.bar_new_returning"
gaBarPages = "ga.bar_pages"
- gaBarCountry = "ga.bar_country"
+ gaBarCountries = "ga.bar_countries"
+ gaBarDevices = "ga.bar_devices"
gaTablePages = "ga.table_pages"
gaTableTrafficSources = "ga.table_traffic_sources"
gaTable = "ga.table"
@@ -69,13 +70,15 @@ func (g *gaWidget) CreateWidgets(widget Widget, tui *Tui) (err error) {
case gaTableTrafficSources:
err = g.trafficSource(widget)
case gaBarNewReturning:
- err = g.newVsReturning(widget)
+ err = g.stackedBarNewReturningUsers(widget)
+ case gaBarDevices:
+ err = g.stackedBarDevices(widget)
case gaBarReturning:
err = g.barReturning(widget)
case gaBarPages:
err = g.barPages(widget)
- case gaBarCountry:
- err = g.barCountry(widget)
+ case gaBarCountries:
+ err = g.barCountries(widget)
case gaBarBounces:
err = g.barBounces(widget)
case gaTable:
@@ -209,7 +212,7 @@ func (g *gaWidget) barPages(widget Widget) (err error) {
return g.barMetric(widget, platform.XHeaderTime)
}
-func (g *gaWidget) barCountry(widget Widget) (err error) {
+func (g *gaWidget) barCountries(widget Widget) (err error) {
if widget.Options == nil {
widget.Options = map[string]string{}
}
@@ -446,7 +449,7 @@ func (g *gaWidget) trafficSource(widget Widget) (err error) {
return g.table(widget, "Source")
}
-func (g *gaWidget) newVsReturning(widget Widget) error {
+func (g *gaWidget) stackedBarNewReturningUsers(widget Widget) error {
if widget.Options == nil {
widget.Options = map[string]string{}
}
@@ -456,6 +459,16 @@ func (g *gaWidget) newVsReturning(widget Widget) error {
return g.stackedBar(widget)
}
+func (g *gaWidget) stackedBarDevices(widget Widget) error {
+ if widget.Options == nil {
+ widget.Options = map[string]string{}
+ }
+
+ widget.Options[optionDimensions] = "device_category"
+
+ return g.stackedBar(widget)
+}
+
func (g *gaWidget) stackedBar(widget Widget) error {
// defaults
sd := "7_days_ago"
@@ -493,7 +506,7 @@ func (g *gaWidget) stackedBar(widget Widget) error {
}
// this should return new and ret instead of a unique slice val...
- dim, new, ret, err := g.analytics.StackedBar(
+ dim, val, err := g.analytics.StackedBar(
platform.AnalyticValues{
ViewID: g.viewID,
StartDate: startDate.Format(gaTimeFormat),
@@ -507,30 +520,37 @@ func (g *gaWidget) stackedBar(widget Widget) error {
return err
}
- var data [8][]int
- // need to fill data with []int containing 0
- for i := 0; i < 8; i++ {
- for j := 0; j < len(ret); j++ {
- data[i] = append(data[i], 0)
- }
- }
- data[0] = new
- data[1] = ret
-
- colors := []uint16{blue, green}
+ // Only support 5 different colors for now
+ colors := []uint16{blue, green, yellow, red, magenta}
if _, ok := widget.Options[optionFirstColor]; ok {
colors[0] = colorLookUp[widget.Options[optionFirstColor]]
}
if _, ok := widget.Options[optionSecondColor]; ok {
colors[1] = colorLookUp[widget.Options[optionSecondColor]]
}
+ if _, ok := widget.Options[optionThirdColor]; ok {
+ colors[2] = colorLookUp[widget.Options[optionThirdColor]]
+ }
+ if _, ok := widget.Options[optionFourthColor]; ok {
+ colors[3] = colorLookUp[widget.Options[optionFourthColor]]
+ }
+ if _, ok := widget.Options[optionFifthColor]; ok {
+ colors[4] = colorLookUp[widget.Options[optionFifthColor]]
+ }
- title := fmt.Sprintf(
- " %s: Returning (%s) vs New (%s) ",
- strings.Trim(strings.Title(metric), "_"),
- colorStr(colors[0]),
- colorStr(colors[1]),
- )
+ var data [8][]int
+ title := fmt.Sprintf(strings.Trim(strings.Title(metric), "_")) + " - "
+ count := 0
+ for k, v := range val {
+ // need to fill data with []int containing 0
+ for j := 0; j < len(val[k]); j++ {
+ data[count] = append(data[count], 0)
+ }
+ data[count] = v
+ title += fmt.Sprintf("/ %s (%s) ", k, colorStr(colors[count]))
+
+ count++
+ }
if _, ok := widget.Options[optionTitle]; ok {
title = widget.Options[optionTitle]
}
diff --git a/internal/platform/ga.go b/internal/platform/ga.go
index 20c1ea9..abf01b2 100644
--- a/internal/platform/ga.go
+++ b/internal/platform/ga.go
@@ -50,10 +50,11 @@ var mappingTimePeriod = map[string][]string{
}
var mappingDimensions = map[string]string{
- "page_path": "ga:pagePath",
- "traffic_source": "ga:source",
- "user_type": "ga:userType",
- "country": "ga:country",
+ "page_path": "ga:pagePath",
+ "traffic_source": "ga:source",
+ "user_type": "ga:userType",
+ "device_category": "ga:deviceCategory",
+ "country": "ga:country",
}
var mappingHeader = map[string]string{
@@ -166,7 +167,7 @@ func (c *Analytics) BarMetric(val AnalyticValues) ([]string, []int, error) {
formater := formatBar
for _, v := range val.Dimensions {
- // TODO - looks weird. We should maybe pass the formater in this function? Extract "user_returning" case in new function?
+ // Special formater - depends on a string returned by API
if v == "user_returning" {
formater = formatBarReturning
}
@@ -327,7 +328,7 @@ func (c *Analytics) Table(
}
// StackedBar returns one dimension set linked with multiple values.
-func (c *Analytics) StackedBar(an AnalyticValues) (dim []string, new []int, ret []int, err error) {
+func (c *Analytics) StackedBar(an AnalyticValues) (dim []string, values map[string][]int, err error) {
d := mapDimensions(an.Dimensions)
tm := mapTimePeriod(an.TimePeriod)
@@ -359,7 +360,7 @@ func (c *Analytics) StackedBar(an AnalyticValues) (dim []string, new []int, ret
resp, err := c.service.Reports.BatchGet(req).Do()
if err != nil {
- return nil, nil, nil, errors.Wrapf(
+ return nil, nil, errors.Wrapf(
err,
"can't get stacked bar data from google analytics with start data %s / end_date %s",
an.StartDate,
@@ -367,11 +368,12 @@ func (c *Analytics) StackedBar(an AnalyticValues) (dim []string, new []int, ret
)
}
+ // Always date on header x-axis
formater := func(dim []string) string {
return dim[1] + "-" + dim[2]
}
- return formatNewReturning(resp.Reports, formater)
+ return formatStackedBar(resp.Reports, formater)
}
// formatBar to return one slice of dimension which elements are all linked with the elements of another slice with the values.
@@ -466,14 +468,16 @@ func formatTable(
return dim, u
}
-func formatNewReturning(
+func formatStackedBar(
reps []*ga.Report,
dimFormater func(dim []string) string,
-) (dim []string, new []int, ret []int, err error) {
+) (dim []string, values map[string][]int, err error) {
+ values = make(map[string][]int)
for _, v := range reps {
for l := 0; l < len(v.Data.Rows); l++ {
- if v.Data.Rows[l].Dimensions[0] == newVisitor {
- dim = append(dim, dimFormater(v.Data.Rows[l].Dimensions))
+ d := dimFormater(v.Data.Rows[l].Dimensions)
+ if !inArray(dim, d) {
+ dim = append(dim, d)
}
for m := 0; m < len(v.Data.Rows[l].Metrics); m++ {
@@ -481,18 +485,24 @@ func formatNewReturning(
var vu int64
if vu, err = strconv.ParseInt(value, 0, 0); err != nil {
- return nil, nil, nil, err
- }
- if v.Data.Rows[l].Dimensions[0] == newVisitor {
- new = append(new, int(vu))
- } else {
- ret = append(ret, int(vu))
+ return nil, nil, err
}
+ header := v.Data.Rows[l].Dimensions[0]
+ values[header] = append(values[header], int(vu))
}
}
}
- return dim, new, ret, nil
+ return dim, values, nil
+}
+
+func inArray(haystack []string, needle string) bool {
+ for _, v := range haystack {
+ if v == needle {
+ return true
+ }
+ }
+ return false
}
// The map functions map the properties of the application to the Google Analytics API params.
diff --git a/internal/platform/ga_test.go b/internal/platform/ga_test.go
index 8b43b4f..0bd5185 100644
--- a/internal/platform/ga_test.go
+++ b/internal/platform/ga_test.go
@@ -91,11 +91,10 @@ func Test_Format(t *testing.T) {
}
}
-func Test_FormatNewReturning(t *testing.T) {
+func Test_FormatStackedBar(t *testing.T) {
testCases := []struct {
name string
- new []int
- ret []int
+ values map[string][]int
fixtureFile string
expectedDim []string
formater func([]string) string
@@ -103,15 +102,17 @@ func Test_FormatNewReturning(t *testing.T) {
}{
{
name: "format new vs returning",
- new: []int{
- 11245,
- 13966,
- 13804,
- },
- ret: []int{
- 1386,
- 1472,
- 1633,
+ values: map[string][]int{
+ newVisitor: {
+ 11245,
+ 13966,
+ 13804,
+ },
+ returningVisitor: {
+ 1386,
+ 1472,
+ 1633,
+ },
},
expectedDim: []string{
"2019-04",
@@ -122,6 +123,22 @@ func Test_FormatNewReturning(t *testing.T) {
formater: func(dim []string) string { return dim[1] + "-" + dim[2] },
wantErr: false,
},
+ {
+ name: "format bar devices",
+ values: map[string][]int{
+ "desktop": {11309, 10239, 1454},
+ "mobile": {6916, 3050, 359},
+ "tablet": {210, 92, 16},
+ },
+ expectedDim: []string{
+ "2020-02",
+ "2020-03",
+ "2020-04",
+ },
+ fixtureFile: "./testdata/fixtures/ga_bar_devices.json",
+ formater: func(dim []string) string { return dim[1] + "-" + dim[2] },
+ wantErr: false,
+ },
}
for _, tc := range testCases {
@@ -133,7 +150,7 @@ func Test_FormatNewReturning(t *testing.T) {
t.Error(err)
}
- dim, new, ret, err := formatNewReturning(r.Reports, tc.formater)
+ dim, values, err := formatStackedBar(r.Reports, tc.formater)
if (err != nil) != tc.wantErr {
t.Errorf("Error '%v' even if wantErr is %t", err, tc.wantErr)
return
@@ -143,12 +160,8 @@ func Test_FormatNewReturning(t *testing.T) {
t.Errorf("Expected %v, actual %v", tc.expectedDim, dim)
}
- if tc.wantErr == false && !reflect.DeepEqual(new, tc.new) {
- t.Errorf("Expected %v, actual %v", tc.new, new)
- }
-
- if tc.wantErr == false && !reflect.DeepEqual(ret, tc.ret) {
- t.Errorf("Expected %v, actual %v", tc.ret, ret)
+ if tc.wantErr == false && !reflect.DeepEqual(values, tc.values) {
+ t.Errorf("Expected %v, actual %v", tc.values, values)
}
})
}
diff --git a/internal/platform/termui.go b/internal/platform/termui.go
index c993bd4..bf227ef 100644
--- a/internal/platform/termui.go
+++ b/internal/platform/termui.go
@@ -153,7 +153,11 @@ func (t *termUI) StackedBarChart(
bc.DataLabels = dimensions
bc.TextColor = termui.Attribute(fg)
bc.BorderFg = termui.Attribute(bd)
- bc.BarColor = [8]termui.Attribute{termui.Attribute(colors[0]), termui.Attribute(colors[1])}
+ bc.BarColor = [8]termui.Attribute{}
+
+ for k, v := range colors {
+ bc.BarColor[k] = termui.Attribute(v)
+ }
bc.NumColor = [8]termui.Attribute{termui.Attribute(nc), termui.Attribute(nc)}
t.widgets = append(t.widgets, bc)
diff --git a/internal/platform/testdata/fixtures/ga_bar_devices.json b/internal/platform/testdata/fixtures/ga_bar_devices.json
new file mode 100644
index 0000000..cb098ce
--- /dev/null
+++ b/internal/platform/testdata/fixtures/ga_bar_devices.json
@@ -0,0 +1,173 @@
+{
+ "reports": [
+ {
+ "columnHeader": {
+ "dimensions": [
+ "ga:deviceCategory",
+ "ga:year",
+ "ga:month"
+ ],
+ "metricHeader": {
+ "metricHeaderEntries": [
+ {
+ "name": "ga:sessions",
+ "type": "INTEGER"
+ }
+ ]
+ }
+ },
+ "data": {
+ "maximums": [
+ {
+ "values": [
+ "11309"
+ ]
+ }
+ ],
+ "minimums": [
+ {
+ "values": [
+ "16"
+ ]
+ }
+ ],
+ "rowCount": 9,
+ "rows": [
+ {
+ "dimensions": [
+ "desktop",
+ "2020",
+ "02"
+ ],
+ "metrics": [
+ {
+ "values": [
+ "11309"
+ ]
+ }
+ ]
+ },
+ {
+ "dimensions": [
+ "desktop",
+ "2020",
+ "03"
+ ],
+ "metrics": [
+ {
+ "values": [
+ "10239"
+ ]
+ }
+ ]
+ },
+ {
+ "dimensions": [
+ "desktop",
+ "2020",
+ "04"
+ ],
+ "metrics": [
+ {
+ "values": [
+ "1454"
+ ]
+ }
+ ]
+ },
+ {
+ "dimensions": [
+ "mobile",
+ "2020",
+ "02"
+ ],
+ "metrics": [
+ {
+ "values": [
+ "6916"
+ ]
+ }
+ ]
+ },
+ {
+ "dimensions": [
+ "mobile",
+ "2020",
+ "03"
+ ],
+ "metrics": [
+ {
+ "values": [
+ "3050"
+ ]
+ }
+ ]
+ },
+ {
+ "dimensions": [
+ "mobile",
+ "2020",
+ "04"
+ ],
+ "metrics": [
+ {
+ "values": [
+ "359"
+ ]
+ }
+ ]
+ },
+ {
+ "dimensions": [
+ "tablet",
+ "2020",
+ "02"
+ ],
+ "metrics": [
+ {
+ "values": [
+ "210"
+ ]
+ }
+ ]
+ },
+ {
+ "dimensions": [
+ "tablet",
+ "2020",
+ "03"
+ ],
+ "metrics": [
+ {
+ "values": [
+ "92"
+ ]
+ }
+ ]
+ },
+ {
+ "dimensions": [
+ "tablet",
+ "2020",
+ "04"
+ ],
+ "metrics": [
+ {
+ "values": [
+ "16"
+ ]
+ }
+ ]
+ }
+ ],
+ "totals": [
+ {
+ "values": [
+ "33645"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/internal/tui.go b/internal/tui.go
index db56a6e..a1a3ea4 100644
--- a/internal/tui.go
+++ b/internal/tui.go
@@ -36,6 +36,10 @@ const (
optionFirstColor = "first_color"
optionSecondColor = "second_color"
+ optionThirdColor = "third_color"
+ optionFourthColor = "fourth_color"
+ optionFifthColor = "fifth_color"
+ optionSixthColor = "sixth_color"
optionHeight = "height"