diff --git a/glide.lock b/glide.lock index 50f3c83c3b4279bc7845c4596f3f40f40351a8c5..bbfcb979be3e03f18ad0861cd567063fdb0f89fb 100644 --- a/glide.lock +++ b/glide.lock @@ -1,8 +1,21 @@ -hash: e3afe7d6be6078ab261a84bcc69e2f87d9805c7553025546d12074b9926fd288 -updated: 2018-05-24T12:12:33.899483528+02:00 +hash: 096846ddf934eee04d5e78003271d70eae05b8167e2611f72d6c34c187689b27 +updated: 2018-05-24T18:48:12.125295149+02:00 imports: +- name: github.com/go-redis/redis + version: 0f9028adf0837cf93c9705817493e5f6997cf026 - name: github.com/lib/pq version: 90697d60dd844d5ef6ff15135d0203f65d2f53b8 subpackages: - oid +- name: gopkg.in/bsm/ratelimit.v1 + version: db14e161995a5177acef654cb0dd785e8ee8bc22 +- name: gopkg.in/redis.v4 + version: c938162545c57136fa59879746bc65d0f1db3d1e + subpackages: + - internal + - internal/consistenthash + - internal/errors + - internal/hashtag + - internal/pool + - internal/proto testImports: [] diff --git a/glide.yaml b/glide.yaml index c1239165b972e041b5a29a68e6efaa398d562c1b..dd7f22ab2dc1ef4f9ad4f551adc11dcc0a50f6b5 100644 --- a/glide.yaml +++ b/glide.yaml @@ -1,3 +1,5 @@ package: git.kuschku.de/justjanne/statsbot import: -- package: github.com/lib/pq \ No newline at end of file +- package: github.com/lib/pq +- package: github.com/go-redis/redis + version: ^6.11.0 \ No newline at end of file diff --git a/main.go b/main.go index 32cad87e117ba647bce5f462d7cceaf086c0bcd8..c861ead7bd443b220e9c3486cd722ca4a22ef0c6 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,10 @@ package main import ( "database/sql" + "encoding/json" "fmt" _ "github.com/lib/pq" + "gopkg.in/redis.v4" "html/template" "net/http" "os" @@ -16,6 +18,7 @@ const DEBUG = true type Config struct { Database DatabaseConfig + Redis RedisConfig } type DatabaseConfig struct { @@ -23,12 +26,20 @@ type DatabaseConfig struct { Url string } +type RedisConfig struct { + Address string + Password string +} + func NewConfigFromEnv() Config { config := Config{} config.Database.Format = os.Getenv("KSTATS_DATABASE_TYPE") config.Database.Url = os.Getenv("KSTATS_DATABASE_URL") + config.Redis.Address = os.Getenv("KSTATS_REDIS_ADDRESS") + config.Redis.Password = os.Getenv("KSTATS_REDIS_PASSWORD") + return config } @@ -107,6 +118,11 @@ func handleError(err error) { func main() { config := NewConfigFromEnv() + redisClient := redis.NewClient(&redis.Options{ + Addr: config.Redis.Address, + Password: config.Redis.Password, + }) + db, err := sql.Open(config.Database.Format, config.Database.Url) if err != nil { panic(err) @@ -116,100 +132,113 @@ func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { _, channel := path.Split(r.URL.Path) if strings.HasPrefix(channel, "#") { - channelData := ChannelData{} - err = db.QueryRow("SELECT id, channel FROM channels WHERE channel ILIKE $1", channel).Scan(&channelData.Id, &channelData.Name) - if err != nil { - handleError(err) - return - } - err = db.QueryRow("SELECT COUNT(*), SUM(words), AVG(words), AVG(characters) FROM messages WHERE channel = $1", channelData.Id).Scan(&channelData.Lines, &channelData.Words, &channelData.WordsPerLine, &channelData.CharactersPerLine) - if err != nil { - handleError(err) - return + var channelData ChannelData + data, err := redisClient.Get(channel).Bytes() + if err == nil { + err = json.Unmarshal(data, channelData) } - channelData.Users, err = retrieveUsers(db, channelData.Id) if err != nil { - handleError(err) - return + channelData, err = buildChannelData(db, channel) + if err != nil { + handleError(err) + return + } + data, err = json.Marshal(channelData) + err = redisClient.Set(channel, data, time.Minute*5).Err() + if err != nil { + handleError(err) + return + } } - channelData.Questions, err = retrievePercentageStats(db, channelData.Id, "question") + err = formatTemplate(w, "statistics", channelData) if err != nil { handleError(err) return } + } + }) - channelData.Exclamations, err = retrievePercentageStats(db, channelData.Id, "exclamation") - if err != nil { - handleError(err) - return - } + http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("OK")) + }) - channelData.Caps, err = retrievePercentageStats(db, channelData.Id, "caps") - if err != nil { - handleError(err) - return - } + err = http.ListenAndServe(":8080", nil) + if err != nil { + panic(err) + } +} - channelData.EmojiHappy, err = retrievePercentageStats(db, channelData.Id, "emoji_happy") - if err != nil { - handleError(err) - return - } +func buildChannelData(db *sql.DB, channel string) (channelData ChannelData, err error) { + fmt.Printf("Recomputing channel data for %s\n", channel) - channelData.EmojiSad, err = retrievePercentageStats(db, channelData.Id, "emoji_sad") - if err != nil { - handleError(err) - return - } + err = db.QueryRow("SELECT id, channel FROM channels WHERE channel ILIKE $1", channel).Scan(&channelData.Id, &channelData.Name) + if err != nil { + return + } - channelData.LongestLines, err = retrieveLongestLines(db, channelData.Id) - if err != nil { - handleError(err) - return - } + err = db.QueryRow("SELECT COUNT(*), SUM(words), AVG(words), AVG(characters) FROM messages WHERE channel = $1", channelData.Id).Scan(&channelData.Lines, &channelData.Words, &channelData.WordsPerLine, &channelData.CharactersPerLine) + if err != nil { + return + } - channelData.ShortestLines, err = retrieveShortestLines(db, channelData.Id) - if err != nil { - handleError(err) - return - } + channelData.Users, err = retrieveUsers(db, channelData.Id) + if err != nil { + return + } - channelData.TotalWords, err = retrieveTotalWords(db, channelData.Id) - if err != nil { - handleError(err) - return - } + channelData.Questions, err = retrievePercentageStats(db, channelData.Id, "question") + if err != nil { + return + } - channelData.References, err = retrieveReferences(db, channelData.Id) - if err != nil { - handleError(err) - return - } + channelData.Exclamations, err = retrievePercentageStats(db, channelData.Id, "exclamation") + if err != nil { + return + } - channelData.AverageWords, err = retrieveAverageWords(db, channelData.Id) - if err != nil { - handleError(err) - return - } + channelData.Caps, err = retrievePercentageStats(db, channelData.Id, "caps") + if err != nil { + return + } - err = formatTemplate(w, "statistics", channelData) - if err != nil { - handleError(err) - return - } - } - }) + channelData.EmojiHappy, err = retrievePercentageStats(db, channelData.Id, "emoji_happy") + if err != nil { + return + } - http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("OK")) - }) + channelData.EmojiSad, err = retrievePercentageStats(db, channelData.Id, "emoji_sad") + if err != nil { + return + } - err = http.ListenAndServe(":8080", nil) + channelData.LongestLines, err = retrieveLongestLines(db, channelData.Id) if err != nil { - panic(err) + return } + + channelData.ShortestLines, err = retrieveShortestLines(db, channelData.Id) + if err != nil { + return + } + + channelData.TotalWords, err = retrieveTotalWords(db, channelData.Id) + if err != nil { + return + } + + channelData.References, err = retrieveReferences(db, channelData.Id) + if err != nil { + return + } + + channelData.AverageWords, err = retrieveAverageWords(db, channelData.Id) + if err != nil { + return + } + + return } func retrievePercentageStats(db *sql.DB, channel int, stats string) ([]FloatEntry, error) {