stats: Add a day categorization above all others

This commit is contained in:
cws 2017-06-05 21:20:08 -04:00
parent de19f6a4e9
commit 4213243744
2 changed files with 62 additions and 44 deletions

View File

@ -31,11 +31,19 @@ func New(bot bot.Bot) *StatsPlugin {
} }
type stat struct { type stat struct {
// date formatted: "2006-01-02"
day string
// category
bucket string bucket string
// specific unique individual
key string key string
val value val value
} }
func mkDay() string {
return time.Now().Format("2006-01-02")
}
// The value type is here in the future growth case that we might want to put a // The value type is here in the future growth case that we might want to put a
// struct of more interesting information into the DB // struct of more interesting information into the DB
type value int type value int
@ -53,13 +61,16 @@ func valueFromBytes(b []byte) (value, error) {
type stats []stat type stats []stat
func mkStat(bucket, key, val []byte) (stat, error) { // mkStat converts raw data to a stat struct
// Expected a string representation of the date formatted: "2006-01-02"
func mkStat(day string, bucket, key, val []byte) (stat, error) {
v, err := valueFromBytes(val) v, err := valueFromBytes(val)
if err != nil { if err != nil {
log.Printf("mkStat: error getting value from bytes: %s", err) log.Printf("mkStat: error getting value from bytes: %s", err)
return stat{}, err return stat{}, err
} }
return stat{ return stat{
day: day,
bucket: string(bucket), bucket: string(bucket),
key: string(key), key: string(key),
val: v, val: v,
@ -71,7 +82,9 @@ func (v value) add(other value) value {
return v + other return v + other
} }
func statFromDB(path, bucket, key string) (stat, error) { // statFromDB takes a location specification and returns the data at that path
// Expected a string representation of the date formatted: "2006-01-02"
func statFromDB(path, day, bucket, key string) (stat, error) {
db, err := bolt.Open(path, 0600, &bolt.Options{ db, err := bolt.Open(path, 0600, &bolt.Options{
Timeout: 1 * time.Second, Timeout: 1 * time.Second,
}) })
@ -90,11 +103,17 @@ func statFromDB(path, bucket, key string) (stat, error) {
} }
defer tx.Rollback() defer tx.Rollback()
b, err := tx.CreateBucketIfNotExists(buk) d, err := tx.CreateBucketIfNotExists([]byte(day))
if err != nil { if err != nil {
log.Println("statFromDB: Error creating the bucket") log.Println("statFromDB: Error creating the bucket")
return stat{}, err return stat{}, err
} }
b, err := d.CreateBucketIfNotExists(buk)
if err != nil {
log.Println("statFromDB: Error creating the bucket")
return stat{}, err
}
v := b.Get(k) v := b.Get(k)
if err := tx.Commit(); err != nil { if err := tx.Commit(); err != nil {
@ -103,12 +122,13 @@ func statFromDB(path, bucket, key string) (stat, error) {
} }
if v == nil { if v == nil {
return stat{bucket, key, 0}, nil return stat{day, bucket, key, 0}, nil
} }
return mkStat(buk, k, v) return mkStat(day, buk, k, v)
} }
// toDB takes a stat and records it, adding to the value in the DB if necessary
func (s stats) toDB(path string) error { func (s stats) toDB(path string) error {
db, err := bolt.Open(path, 0600, &bolt.Options{Timeout: 1 * time.Second}) db, err := bolt.Open(path, 0600, &bolt.Options{Timeout: 1 * time.Second})
if err != nil { if err != nil {
@ -119,7 +139,12 @@ func (s stats) toDB(path string) error {
for _, stat := range s { for _, stat := range s {
err = db.Update(func(tx *bolt.Tx) error { err = db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte(stat.bucket)) d, err := tx.CreateBucketIfNotExists([]byte(stat.day))
if err != nil {
log.Println("toDB: Error creating bucket")
return err
}
b, err := d.CreateBucketIfNotExists([]byte(stat.bucket))
if err != nil { if err != nil {
log.Println("toDB: Error creating bucket") log.Println("toDB: Error creating bucket")
return err return err
@ -192,24 +217,24 @@ func (p *StatsPlugin) RegisterWeb() *string {
} }
func (p *StatsPlugin) mkUserStat(message msg.Message) stats { func (p *StatsPlugin) mkUserStat(message msg.Message) stats {
return stats{stat{"user", message.User.Name, 1}} return stats{stat{mkDay(), "user", message.User.Name, 1}}
} }
func (p *StatsPlugin) mkHourStat(message msg.Message) stats { func (p *StatsPlugin) mkHourStat(message msg.Message) stats {
hr := time.Now().Hour() hr := time.Now().Hour()
return stats{stat{"user", string(hr), 1}} return stats{stat{mkDay(), "user", string(hr), 1}}
} }
func (p *StatsPlugin) mkSightingStat(message msg.Message) stats { func (p *StatsPlugin) mkSightingStat(message msg.Message) stats {
stats := stats{} stats := stats{}
for _, name := range p.bot.Config().Stats.Sightings { for _, name := range p.bot.Config().Stats.Sightings {
if strings.Contains(message.Body, name+" sighting") { if strings.Contains(message.Body, name+" sighting") {
stats = append(stats, stat{"sighting", name, 1}) stats = append(stats, stat{mkDay(), "sighting", name, 1})
} }
} }
return stats return stats
} }
func (p *StatsPlugin) mkChannelStat(message msg.Message) stats { func (p *StatsPlugin) mkChannelStat(message msg.Message) stats {
return stats{stat{"channel", message.Channel, 1}} return stats{stat{mkDay(), "channel", message.Channel, 1}}
} }

View File

@ -47,10 +47,12 @@ func TestWithDB(t *testing.T) {
rmDB(t) rmDB(t)
t.Run("TestDBReadWrite", func(t *testing.T) { t.Run("TestDBReadWrite", func(t *testing.T) {
day := mkDay()
bucket := "testBucket" bucket := "testBucket"
key := "testKey" key := "testKey"
expected := stats{stat{ expected := stats{stat{
day,
bucket, bucket,
key, key,
1, 1,
@ -59,7 +61,7 @@ func TestWithDB(t *testing.T) {
err := expected.toDB(dbPath) err := expected.toDB(dbPath)
assert.Nil(t, err) assert.Nil(t, err)
actual, err := statFromDB(dbPath, bucket, key) actual, err := statFromDB(dbPath, day, bucket, key)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, actual, expected[0]) assert.Equal(t, actual, expected[0])
@ -69,11 +71,13 @@ func TestWithDB(t *testing.T) {
rmDB(t) rmDB(t)
t.Run("TestDBAddStatInLoop", func(t *testing.T) { t.Run("TestDBAddStatInLoop", func(t *testing.T) {
day := mkDay()
bucket := "testBucket" bucket := "testBucket"
key := "testKey" key := "testKey"
expected := value(25) expected := value(25)
statPack := stats{stat{ statPack := stats{stat{
day,
bucket, bucket,
key, key,
5, 5,
@ -84,7 +88,7 @@ func TestWithDB(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
} }
actual, err := statFromDB(dbPath, bucket, key) actual, err := statFromDB(dbPath, day, bucket, key)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, actual.val, expected) assert.Equal(t, actual.val, expected)
@ -93,42 +97,25 @@ func TestWithDB(t *testing.T) {
rmDB(t) rmDB(t)
t.Run("TestDBAddStats", func(t *testing.T) { t.Run("TestDBAddStats", func(t *testing.T) {
day := mkDay()
bucket := "testBucket" bucket := "testBucket"
key := "testKey" key := "testKey"
expected := value(5) expected := value(5)
statPack := stats{ statPack := stats{}
stat{ for i := 0; i < 5; i++ {
statPack = append(statPack, stat{
day,
bucket, bucket,
key, key,
1, 1,
}, })
stat{
bucket,
key,
1,
},
stat{
bucket,
key,
1,
},
stat{
bucket,
key,
1,
},
stat{
bucket,
key,
1,
},
} }
err := statPack.toDB(dbPath) err := statPack.toDB(dbPath)
assert.Nil(t, err) assert.Nil(t, err)
actual, err := statFromDB(dbPath, bucket, key) actual, err := statFromDB(dbPath, day, bucket, key)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, actual.val, expected) assert.Equal(t, actual.val, expected)
@ -151,6 +138,7 @@ func makeMessage(payload string) msg.Message {
} }
func testUserCounter(t *testing.T, count int) { func testUserCounter(t *testing.T, count int) {
day := mkDay()
expected := value(count) expected := value(count)
mb := bot.NewMockBot() mb := bot.NewMockBot()
mb.Cfg.Stats.DBPath = dbPath mb.Cfg.Stats.DBPath = dbPath
@ -164,7 +152,7 @@ func testUserCounter(t *testing.T, count int) {
_, err := os.Stat(dbPath) _, err := os.Stat(dbPath)
assert.Nil(t, err) assert.Nil(t, err)
stat, err := statFromDB(mb.Config().Stats.DBPath, "user", "tester") stat, err := statFromDB(mb.Config().Stats.DBPath, day, "user", "tester")
assert.Nil(t, err) assert.Nil(t, err)
actual := stat.val actual := stat.val
assert.Equal(t, actual, expected) assert.Equal(t, actual, expected)
@ -175,6 +163,7 @@ func TestMessages(t *testing.T) {
assert.NotNil(t, err) assert.NotNil(t, err)
t.Run("TestOneUserCounter", func(t *testing.T) { t.Run("TestOneUserCounter", func(t *testing.T) {
day := mkDay()
count := 5 count := 5
expected := value(count) expected := value(count)
mb := bot.NewMockBot() mb := bot.NewMockBot()
@ -189,7 +178,7 @@ func TestMessages(t *testing.T) {
_, err := os.Stat(dbPath) _, err := os.Stat(dbPath)
assert.Nil(t, err) assert.Nil(t, err)
stat, err := statFromDB(mb.Config().Stats.DBPath, "user", "tester") stat, err := statFromDB(mb.Config().Stats.DBPath, day, "user", "tester")
assert.Nil(t, err) assert.Nil(t, err)
actual := stat.val actual := stat.val
assert.Equal(t, actual, expected) assert.Equal(t, actual, expected)
@ -198,6 +187,7 @@ func TestMessages(t *testing.T) {
rmDB(t) rmDB(t)
t.Run("TestTenUserCounter", func(t *testing.T) { t.Run("TestTenUserCounter", func(t *testing.T) {
day := mkDay()
count := 5 count := 5
expected := value(count) expected := value(count)
mb := bot.NewMockBot() mb := bot.NewMockBot()
@ -212,7 +202,7 @@ func TestMessages(t *testing.T) {
_, err := os.Stat(dbPath) _, err := os.Stat(dbPath)
assert.Nil(t, err) assert.Nil(t, err)
stat, err := statFromDB(mb.Config().Stats.DBPath, "user", "tester") stat, err := statFromDB(mb.Config().Stats.DBPath, day, "user", "tester")
assert.Nil(t, err) assert.Nil(t, err)
actual := stat.val actual := stat.val
assert.Equal(t, actual, expected) assert.Equal(t, actual, expected)
@ -221,6 +211,7 @@ func TestMessages(t *testing.T) {
rmDB(t) rmDB(t)
t.Run("TestChannelCounter", func(t *testing.T) { t.Run("TestChannelCounter", func(t *testing.T) {
day := mkDay()
count := 5 count := 5
expected := value(count) expected := value(count)
mb := bot.NewMockBot() mb := bot.NewMockBot()
@ -235,7 +226,7 @@ func TestMessages(t *testing.T) {
_, err := os.Stat(dbPath) _, err := os.Stat(dbPath)
assert.Nil(t, err) assert.Nil(t, err)
stat, err := statFromDB(mb.Config().Stats.DBPath, "channel", "test") stat, err := statFromDB(mb.Config().Stats.DBPath, day, "channel", "test")
assert.Nil(t, err) assert.Nil(t, err)
actual := stat.val actual := stat.val
assert.Equal(t, actual, expected) assert.Equal(t, actual, expected)
@ -244,6 +235,7 @@ func TestMessages(t *testing.T) {
rmDB(t) rmDB(t)
t.Run("TestSightingCounter", func(t *testing.T) { t.Run("TestSightingCounter", func(t *testing.T) {
day := mkDay()
count := 5 count := 5
expected := value(count) expected := value(count)
mb := bot.NewMockBot() mb := bot.NewMockBot()
@ -261,7 +253,7 @@ func TestMessages(t *testing.T) {
_, err := os.Stat(dbPath) _, err := os.Stat(dbPath)
assert.Nil(t, err) assert.Nil(t, err)
stat, err := statFromDB(mb.Config().Stats.DBPath, "sighting", "user") stat, err := statFromDB(mb.Config().Stats.DBPath, day, "sighting", "user")
assert.Nil(t, err) assert.Nil(t, err)
actual := stat.val actual := stat.val
assert.Equal(t, actual, expected) assert.Equal(t, actual, expected)
@ -270,6 +262,7 @@ func TestMessages(t *testing.T) {
rmDB(t) rmDB(t)
t.Run("TestSightingCounterNoResults", func(t *testing.T) { t.Run("TestSightingCounterNoResults", func(t *testing.T) {
day := mkDay()
count := 5 count := 5
expected := value(0) expected := value(0)
mb := bot.NewMockBot() mb := bot.NewMockBot()
@ -287,7 +280,7 @@ func TestMessages(t *testing.T) {
_, err := os.Stat(dbPath) _, err := os.Stat(dbPath)
assert.Nil(t, err) assert.Nil(t, err)
stat, err := statFromDB(mb.Config().Stats.DBPath, "sighting", "user") stat, err := statFromDB(mb.Config().Stats.DBPath, day, "sighting", "user")
assert.Nil(t, err) assert.Nil(t, err)
actual := stat.val actual := stat.val
assert.Equal(t, actual, expected) assert.Equal(t, actual, expected)