Skip to content

Commit

Permalink
Merge pull request #324 from zgoat/bottom-totals
Browse files Browse the repository at this point in the history
Put totals at the bottom, independent Y-axis, redesign horizontal charts
  • Loading branch information
arp242 committed Jul 3, 2020
2 parents 3c61ac5 + aa1efce commit e9c5988
Show file tree
Hide file tree
Showing 37 changed files with 2,051 additions and 2,364 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.markdown
Expand Up @@ -25,6 +25,12 @@ master branch

See http://goatcounter.com/api for details.

- Some redesigns (#324, #315, #321 #320)

The "Totals" is now placed below the Pages; I think it makes more sense there.
The Y-axis for the totals is now also independent. There's also been a quite a
few restylings.

- Make it easier to skip your own views (#290)

Previously this required adding custom code, but now loading any page with
Expand Down
2 changes: 1 addition & 1 deletion cron/browser_stat.go
Expand Up @@ -23,7 +23,7 @@ import (
// 1 | 2019-12-17 | Opera | 9 | 1
func updateBrowserStats(ctx context.Context, hits []goatcounter.Hit) error {
return zdb.TX(ctx, func(ctx context.Context, tx zdb.DB) error {
// Group by day + browser.
// Group by day + browser + version.
type gt struct {
count int
countUnique int
Expand Down
18 changes: 9 additions & 9 deletions cron/browser_stat_test.go
Expand Up @@ -32,13 +32,13 @@ func TestBrowserStats(t *testing.T) {
}

var stats goatcounter.Stats
total, err := stats.ListBrowsers(ctx, now, now)
err = stats.ListBrowsers(ctx, now, now, 10, 0)
if err != nil {
t.Fatal(err)
}

want := `4 -> [{Firefox 3 1} {Chrome 1 0}]`
out := fmt.Sprintf("%d -> %v", total, stats)
want := `{false [{Firefox 3 1 <nil>} {Chrome 1 0 <nil>}]}`
out := fmt.Sprintf("%v", stats)
if want != out {
t.Errorf("\nwant: %s\nout: %s", want, out)
}
Expand All @@ -55,26 +55,26 @@ func TestBrowserStats(t *testing.T) {
}

stats = goatcounter.Stats{}
total, err = stats.ListBrowsers(ctx, now, now)
err = stats.ListBrowsers(ctx, now, now, 10, 0)
if err != nil {
t.Fatal(err)
}

want = `8 -> [{Firefox 7 2} {Chrome 1 0}]`
out = fmt.Sprintf("%d -> %v", total, stats)
want = `{false [{Firefox 7 2 <nil>} {Chrome 1 0 <nil>}]}`
out = fmt.Sprintf("%v", stats)
if want != out {
t.Errorf("\nwant: %s\nout: %s", want, out)
}

// List just Firefox.
stats = goatcounter.Stats{}
total, err = stats.ListBrowser(ctx, "Firefox", now, now)
err = stats.ListBrowser(ctx, "Firefox", now, now)
if err != nil {
t.Fatal(err)
}

want = `7 -> [{Firefox 69 4 1} {Firefox 70 2 0} {Firefox 68 1 1}]`
out = fmt.Sprintf("%d -> %v", total, stats)
want = `{false [{Firefox 68 1 1 <nil>} {Firefox 69 4 1 <nil>} {Firefox 70 2 0 <nil>}]}`
out = fmt.Sprintf("%v", stats)
if want != out {
t.Errorf("\nwant: %s\nout: %s", want, out)
}
Expand Down
7 changes: 3 additions & 4 deletions cron/hit_stat_test.go
Expand Up @@ -28,14 +28,13 @@ func TestHitStats(t *testing.T) {
}...)

var stats goatcounter.HitStats
total, totalUnique, display, displayUnique, more, err := stats.List(
ctx, now.Add(-1*time.Hour), now.Add(1*time.Hour), "", nil, false)
display, displayUnique, more, err := stats.List(ctx, now.Add(-1*time.Hour), now.Add(1*time.Hour), "", nil, false)
if err != nil {
t.Fatal(err)
}

gotT := fmt.Sprintf("%d %d %d %d %t", total, totalUnique, display, displayUnique, more)
wantT := "3 1 3 1 false"
gotT := fmt.Sprintf("%d %d %t", display, displayUnique, more)
wantT := "3 1 false"
if wantT != gotT {
t.Fatalf("wrong totals\ngot: %s\nwant: %s", gotT, wantT)
}
Expand Down
12 changes: 6 additions & 6 deletions cron/location_stat_test.go
Expand Up @@ -31,13 +31,13 @@ func TestLocationStats(t *testing.T) {
}

var stats goatcounter.Stats
total, err := stats.ListLocations(ctx, now, now)
err = stats.ListLocations(ctx, now, now, 10, 0)
if err != nil {
t.Fatal(err)
}

want := `3 -> [{Indonesia 2 0} {Ethiopia 1 1}]`
out := fmt.Sprintf("%d -> %v", total, stats)
want := `{false [{Ethiopia 1 1 <nil>} {Indonesia 2 0 <nil>}]}`
out := fmt.Sprintf("%v", stats)
if want != out {
t.Errorf("\nwant: %s\nout: %s", want, out)
}
Expand All @@ -57,13 +57,13 @@ func TestLocationStats(t *testing.T) {
}

stats = goatcounter.Stats{}
total, err = stats.ListLocations(ctx, now, now)
err = stats.ListLocations(ctx, now, now, 10, 0)
if err != nil {
t.Fatal(err)
}

want = `10 -> [{Ethiopia 5 3} {Indonesia 4 0} {New Zealand 1 0}]`
out = fmt.Sprintf("%d -> %v", total, stats)
want = `{false [{Ethiopia 5 3 <nil>} {Indonesia 4 0 <nil>} {New Zealand 1 0 <nil>}]}`
out = fmt.Sprintf("%v", stats)
if want != out {
t.Errorf("\nwant: %s\nout: %s", want, out)
}
Expand Down
32 changes: 16 additions & 16 deletions cron/size_stat_test.go
Expand Up @@ -34,18 +34,18 @@ func TestSizeStats(t *testing.T) {
}

var stats goatcounter.Stats
total, err := stats.ListSizes(ctx, now, now)
err = stats.ListSizes(ctx, now, now)
if err != nil {
t.Fatal(err)
}

want := `5 -> [{Phones 0 0}
{Large phones, small tablets 1 0}
{Tablets and small laptops 0 0}
{Computer monitors 2 1}
{Computer monitors larger than HD 0 0}
{(unknown) 2 0}]`
out := strings.ReplaceAll(fmt.Sprintf("%d -> %v", total, stats), "} ", "}\n")
want := `{false [{Phones 0 0 <nil>}
{Large phones, small tablets 1 0 <nil>}
{Tablets and small laptops 0 0 <nil>}
{Computer monitors 2 1 <nil>}
{Computer monitors larger than HD 0 0 <nil>}
{(unknown) 2 0 <nil>}]}`
out := strings.ReplaceAll(fmt.Sprintf("%v", stats), "} ", "}\n")
if want != out {
t.Errorf("\nwant:\n%s\nout:\n%s", want, out)
}
Expand All @@ -64,18 +64,18 @@ func TestSizeStats(t *testing.T) {
}

stats = goatcounter.Stats{}
total, err = stats.ListSizes(ctx, now, now)
err = stats.ListSizes(ctx, now, now)
if err != nil {
t.Fatal(err)
}

want = `11 -> [{Phones 1 0}
{Large phones, small tablets 3 0}
{Tablets and small laptops 0 0}
{Computer monitors 4 2}
{Computer monitors larger than HD 0 0}
{(unknown) 3 1}]`
out = strings.ReplaceAll(fmt.Sprintf("%d -> %v", total, stats), "} ", "}\n")
want = `{false [{Phones 1 0 <nil>}
{Large phones, small tablets 3 0 <nil>}
{Tablets and small laptops 0 0 <nil>}
{Computer monitors 4 2 <nil>}
{Computer monitors larger than HD 0 0 <nil>}
{(unknown) 3 1 <nil>}]}`
out = strings.ReplaceAll(fmt.Sprintf("%v", stats), "} ", "}\n")
if want != out {
t.Errorf("\nwant: %s\nout: %s", want, out)
}
Expand Down
7 changes: 3 additions & 4 deletions cron/tasks_test.go
Expand Up @@ -52,14 +52,13 @@ func TestDataRetention(t *testing.T) {
}

var stats goatcounter.HitStats
total, totalUnique, display, displayUnique, more, err := stats.List(
ctx, past.Add(-1*24*time.Hour), now, "", nil, false)
display, displayUnique, more, err := stats.List(ctx, past.Add(-1*24*time.Hour), now, "", nil, false)
if err != nil {
t.Fatal(err)
}

out := fmt.Sprintf("%d %d %d %d %t %v", total, totalUnique, display, displayUnique, more, err)
want := `2 1 2 1 false <nil>`
out := fmt.Sprintf("%d %d %t %v", display, displayUnique, more, err)
want := `2 1 false <nil>`
if out != want {
t.Errorf("\ngot: %s\nwant: %s", out, want)
}
Expand Down
103 changes: 42 additions & 61 deletions gctest/gctest.go
Expand Up @@ -21,7 +21,6 @@ import (
"zgo.at/goatcounter/cron"
"zgo.at/zdb"
"zgo.at/zhttp"
"zgo.at/zlog"
"zgo.at/zstd/zstring"
)

Expand All @@ -41,9 +40,7 @@ var (
// DB starts a new database test.
func DB(t tester) (context.Context, func()) {
t.Helper()

l := zlog.Module("gctest")
//l = l.SetDebug("gctest")
ctx := context.Background()

if db == nil {
var err error
Expand All @@ -53,7 +50,6 @@ func DB(t tester) (context.Context, func()) {
if err != nil {
t.Fatalf("%s → %s", err, out)
}
l = l.Since("createdb")
}

db, err = sqlx.Connect("postgres", "dbname="+dbname+" sslmode=disable password=x")
Expand All @@ -63,34 +59,20 @@ func DB(t tester) (context.Context, func()) {
if err != nil {
t.Fatalf("connect to DB: %s", err)
}
l = l.Since("connect")
ctx = zdb.With(ctx, db)

setupDB(t)

l = l.Since("setupDB")

if cfg.PgSQL {
err = db.Select(&tables, `select c.relname as name
from pg_catalog.pg_class c
left join pg_catalog.pg_namespace n on n.oid = c.relnamespace
where
c.relkind = 'r' and
n.nspname <> 'pg_catalog' and
n.nspname <> 'information_schema' and
n.nspname !~ '^pg_toast' and
pg_catalog.pg_table_is_visible(c.oid);`)
} else {
err = db.Select(&tables, `select name from sqlite_master where type='table'`)
}
tables, err = zdb.ListTables(ctx)
if err != nil {
t.Fatal(err)
}

exclude := []string{"iso_3166_1", "version"}
tables = zstring.Filter(tables, func(t string) bool { return !zstring.Contains(exclude, t) })

l = l.Since("list tables")
} else {
ctx = zdb.With(ctx, db)

q := `delete from %s`
if cfg.PgSQL {
// TODO: takes about 450ms, which is rather long. See if we can
Expand All @@ -103,46 +85,9 @@ func DB(t tester) (context.Context, func()) {
if !cfg.PgSQL {
db.MustExec(`delete from sqlite_sequence`)
}

l = l.Since("truncate")
}
ctx := zdb.With(context.Background(), db)

{
_, err := db.ExecContext(ctx, `insert into sites
(code, plan, settings, created_at) values ('test', 'personal', '{}', $1)`,
goatcounter.Now().Format(zdb.Date))
if err != nil {
t.Fatalf("create site: %s", err)
}
l = l.Since("create site")

var site goatcounter.Site
err = site.ByID(ctx, 1)
if err != nil {
t.Fatalf("get site: %s", err)
}
ctx = goatcounter.WithSite(ctx, &site)
l = l.Since("get site")
}

{
_, err := db.ExecContext(ctx, `insert into users
(site, email, password, created_at) values (1, 'test@example.com', 'xx', $1)`,
goatcounter.Now().Format(zdb.Date))
if err != nil {
t.Fatalf("create site: %s", err)
}
l = l.Since("create user")

var user goatcounter.User
err = user.BySite(ctx, 1)
if err != nil {
t.Fatalf("get user: %s", err)
}
ctx = goatcounter.WithUser(ctx, &user)
l = l.Since("get user")
}
ctx = initData(ctx, t)

return ctx, func() {
goatcounter.Salts.Clear()
Expand Down Expand Up @@ -220,6 +165,42 @@ func setupDB(t tester) {
}
}

func initData(ctx context.Context, t tester) context.Context {
{
_, err := db.ExecContext(ctx, `insert into sites
(code, plan, settings, created_at) values ('test', 'personal', '{}', $1)`,
goatcounter.Now().Format(zdb.Date))
if err != nil {
t.Fatalf("create site: %s", err)
}

var site goatcounter.Site
err = site.ByID(ctx, 1)
if err != nil {
t.Fatalf("get site: %s", err)
}
ctx = goatcounter.WithSite(ctx, &site)
}

{
_, err := db.ExecContext(ctx, `insert into users
(site, email, password, created_at) values (1, 'test@example.com', 'xx', $1)`,
goatcounter.Now().Format(zdb.Date))
if err != nil {
t.Fatalf("create site: %s", err)
}

var user goatcounter.User
err = user.BySite(ctx, 1)
if err != nil {
t.Fatalf("get user: %s", err)
}
ctx = goatcounter.WithUser(ctx, &user)
}

return ctx
}

// StoreHits is a convenient helper to store hits in the DB via Memstore and
// cron.UpdateStats().
func StoreHits(ctx context.Context, t *testing.T, hits ...goatcounter.Hit) []goatcounter.Hit {
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Expand Up @@ -19,14 +19,14 @@ require (
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
golang.org/x/tools v0.0.0-20200519205726-57a9e4404bf7
honnef.co/go/tools v0.0.1-2020.1.4
zgo.at/blackmail v0.0.0-20200527044144-ce30fa1f0a22
zgo.at/errors v0.0.0-20200630050327-695f120d26a1
zgo.at/blackmail v0.0.0-20200703094839-f1e44ef1dbb8
zgo.at/errors v0.0.0-20200630143612-2b870b08fb1d
zgo.at/gadget v0.0.0-20200522205438-64412137d778
zgo.at/guru v1.1.0
zgo.at/isbot v0.0.0-20200627092736-4e2d25be1249
zgo.at/json v0.0.0-20200627042140-d5025253667f
zgo.at/tz v0.0.0-20200520034804-aeba38d94d93
zgo.at/zdb v0.0.0-20200622035304-8fce69315f06
zgo.at/zdb v0.0.0-20200703091242-8c8453108ac7
zgo.at/zhttp v0.0.0-20200630054645-5de78e421358
zgo.at/zlog v0.0.0-20200519105857-4dc5e4ffe04c
zgo.at/zpack v1.0.1
Expand Down
12 changes: 6 additions & 6 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e9c5988

Please sign in to comment.