diff --git a/cmd/aggregate/main.go b/cmd/aggregate/main.go index 54e34d22..4fe8bf50 100644 --- a/cmd/aggregate/main.go +++ b/cmd/aggregate/main.go @@ -75,6 +75,7 @@ func setupDB(db *sql.DB) error { _, err = db.Exec(`CREATE TABLE IF NOT EXISTS UserMovement ( Day TIMESTAMP NOT NULL, Added INTEGER NOT NULL, + Bounced INTEGER NOT NULL, Removed INTEGER NOT NULL )`) if err != nil { @@ -169,20 +170,26 @@ func aggregateUserMovement(db *sql.DB) (int64, error) { day time.Time added int removed int + bounced int } var sumRows []sumRow for t := minTs; t.Before(time.Now().Truncate(24 * time.Hour)); t = t.AddDate(0, 0, 1) { - var added, removed int + var added, removed, bounced int + old := t.Before(time.Now().AddDate(0, 0, -14)) for id, first := range firstSeen { last := lastSeen[id] + if first.Equal(t) && last.Equal(t) && old { + bounced++ + continue + } if first.Equal(t) { added++ } - if last == t && t.Before(time.Now().AddDate(0, 0, -14)) { + if last == t && old { removed++ } } - sumRows = append(sumRows, sumRow{t, added, removed}) + sumRows = append(sumRows, sumRow{t, added, removed, bounced}) } tx, err := db.Begin() @@ -194,7 +201,7 @@ func aggregateUserMovement(db *sql.DB) (int64, error) { return 0, err } for _, r := range sumRows { - if _, err := tx.Exec("INSERT INTO UserMovement (Day, Added, Removed) VALUES ($1, $2, $3)", r.day, r.added, r.removed); err != nil { + if _, err := tx.Exec("INSERT INTO UserMovement (Day, Added, Removed, Bounced) VALUES ($1, $2, $3, $4)", r.day, r.added, r.removed, r.bounced); err != nil { tx.Rollback() return 0, err } diff --git a/cmd/ursrv/main.go b/cmd/ursrv/main.go index 11f3d020..091b5d8b 100644 --- a/cmd/ursrv/main.go +++ b/cmd/ursrv/main.go @@ -576,28 +576,31 @@ func getSummary(db *sql.DB) (summary, error) { } func getMovement(db *sql.DB) ([][]interface{}, error) { - rows, err := db.Query(`SELECT Day, Added, Removed FROM UserMovement WHERE Day > now() - '1 year'::INTERVAL ORDER BY Day`) + rows, err := db.Query(`SELECT Day, Added, Removed, Bounced FROM UserMovement WHERE Day > now() - '1 year'::INTERVAL ORDER BY Day`) if err != nil { return nil, err } defer rows.Close() res := [][]interface{}{ - {"Day", "Joined", "Left"}, + {"Day", "Joined", "Left", "Bounced"}, } for rows.Next() { var day time.Time - var added, removed int - err := rows.Scan(&day, &added, &removed) + var added, removed, bounced int + err := rows.Scan(&day, &added, &removed, &bounced) if err != nil { return nil, err } - row := []interface{}{day.Format("2006-01-02"), added, -removed} + row := []interface{}{day.Format("2006-01-02"), added, -removed, bounced} if removed == 0 { row[2] = nil } + if bounced == 0 { + row[3] = nil + } res = append(res, row) } diff --git a/static/index.html b/static/index.html index d13ec943..c80a2af7 100644 --- a/static/index.html +++ b/static/index.html @@ -70,6 +70,7 @@ found in the LICENSE file. for (var i = 1; i < rows[0].length; i++){ data.addColumn('number', rows[0][i]); } + for (var i = 1; i < rows.length; i++){ rows[i][0] = new Date(rows[i][0]); if (rows[i][1] > 500) { @@ -107,7 +108,7 @@ found in the LICENSE file.

Users Joining and Leaving per Day

- This is the total number of unique users joining and leaving per day. A user is counted as "joined" on first the day their unique ID is seen, and as "left" on the last day the unique ID was seen before a two weeks or longer absence. + This is the total number of unique users joining and leaving per day. A user is counted as "joined" on first the day their unique ID is seen, and as "left" on the last day the unique ID was seen before a two weeks or longer absence. "Bounced" refers to users who joined and left on the same day.