Skip to content
Snippets Groups Projects
Verified Commit 581cb2e9 authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

More statistics

parent 97634c11
Branches
No related tags found
No related merge requests found
...@@ -12,6 +12,8 @@ import ( ...@@ -12,6 +12,8 @@ import (
"time" "time"
) )
const DEBUG = true
type Config struct { type Config struct {
Database DatabaseConfig Database DatabaseConfig
} }
...@@ -47,9 +49,10 @@ func formatTemplate(w http.ResponseWriter, templateName string, data interface{} ...@@ -47,9 +49,10 @@ func formatTemplate(w http.ResponseWriter, templateName string, data interface{}
type ChannelData struct { type ChannelData struct {
Id int Id int
Name string Name string
TotalCharacters int Lines int
TotalWords int Words int
TotalLines int WordsPerLine float64
CharactersPerLine float64
Users []UserData Users []UserData
Questions []FloatEntry Questions []FloatEntry
Exclamations []FloatEntry Exclamations []FloatEntry
...@@ -58,8 +61,9 @@ type ChannelData struct { ...@@ -58,8 +61,9 @@ type ChannelData struct {
EmojiSad []FloatEntry EmojiSad []FloatEntry
LongestLines []FloatEntry LongestLines []FloatEntry
ShortestLines []FloatEntry ShortestLines []FloatEntry
Total []IntEntry TotalWords []TotalEntry
Average float64 AverageWords []FloatEntry
References []ReferenceData
} }
type FloatEntry struct { type FloatEntry struct {
...@@ -72,14 +76,34 @@ type IntEntry struct { ...@@ -72,14 +76,34 @@ type IntEntry struct {
Value int Value int
} }
type TotalEntry struct {
Name string
Value int
Previous string
}
type UserData struct { type UserData struct {
Name string Name string
Total int
Words int
Lines int Lines int
Words int
WordsPerLine float64
LastSeen time.Time LastSeen time.Time
} }
type ReferenceData struct {
Name string
LastUsed string
Count string
}
func handleError(err error) {
if DEBUG {
panic(err)
} else {
handleError(err)
}
}
func main() { func main() {
config := NewConfigFromEnv() config := NewConfigFromEnv()
...@@ -95,73 +119,78 @@ func main() { ...@@ -95,73 +119,78 @@ func main() {
channelData := ChannelData{} channelData := ChannelData{}
err = db.QueryRow("SELECT id, channel FROM channels WHERE channel ILIKE $1", channel).Scan(&channelData.Id, &channelData.Name) err = db.QueryRow("SELECT id, channel FROM channels WHERE channel ILIKE $1", channel).Scan(&channelData.Id, &channelData.Name)
if err != nil { if err != nil {
println(err.Error()) handleError(err)
return return
} }
err = db.QueryRow("SELECT COUNT(*), SUM(characters), SUM(words) FROM messages WHERE channel = $1", channelData.Id).Scan(&channelData.TotalLines, &channelData.TotalCharacters, &channelData.TotalWords) 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 { if err != nil {
println(err.Error()) handleError(err)
return return
} }
result, err := db.Query("SELECT coalesce(users.nick, '[Unknown]'), t.characters, t.words, t.lines, t.lastSeen FROM (SELECT coalesce(groups.\"group\", messages.sender) AS hash, SUM(messages.characters) as characters, SUM(messages.words) as words, COUNT(*) as lines, MAX(messages.time) AS lastSeen FROM messages LEFT JOIN groups ON messages.sender = groups.nick AND groups.channel = 1 WHERE messages.channel = 1 GROUP BY hash ORDER BY characters DESC) t LEFT JOIN users ON t.hash = users.hash LIMIT 20")
channelData.Users, err = retrieveUsers(db, channelData.Id)
if err != nil { if err != nil {
println(err.Error()) handleError(err)
return return
} }
for result.Next() {
var info UserData channelData.Questions, err = retrievePercentageStats(db, channelData.Id, "question")
err := result.Scan(&info.Name, &info.Total, &info.Words, &info.Lines, &info.LastSeen)
if err != nil { if err != nil {
panic(err) handleError(err)
return
} }
channelData.Users = append(channelData.Users, info)
channelData.Exclamations, err = retrievePercentageStats(db, channelData.Id, "exclamation")
if err != nil {
handleError(err)
return
} }
channelData.Questions, err = retrievePercentageStats(db, "question") channelData.Caps, err = retrievePercentageStats(db, channelData.Id, "caps")
if err != nil { if err != nil {
println(err.Error()) handleError(err)
return return
} }
channelData.Exclamations, err = retrievePercentageStats(db, "exclamation") channelData.EmojiHappy, err = retrievePercentageStats(db, channelData.Id, "emoji_happy")
if err != nil { if err != nil {
println(err.Error()) handleError(err)
return return
} }
channelData.Caps, err = retrievePercentageStats(db, "caps") channelData.EmojiSad, err = retrievePercentageStats(db, channelData.Id, "emoji_sad")
if err != nil { if err != nil {
println(err.Error()) handleError(err)
return return
} }
channelData.EmojiHappy, err = retrievePercentageStats(db, "emoji_happy") channelData.LongestLines, err = retrieveLongestLines(db, channelData.Id)
if err != nil { if err != nil {
println(err.Error()) handleError(err)
return return
} }
channelData.EmojiSad, err = retrievePercentageStats(db, "emoji_sad") channelData.ShortestLines, err = retrieveShortestLines(db, channelData.Id)
if err != nil { if err != nil {
println(err.Error()) handleError(err)
return return
} }
channelData.LongestLines, err = retrieveLongestLines(db) channelData.TotalWords, err = retrieveTotalWords(db, channelData.Id)
if err != nil { if err != nil {
println(err.Error()) handleError(err)
return return
} }
channelData.ShortestLines, err = retrieveShortestLines(db) channelData.References, err = retrieveReferences(db, channelData.Id)
if err != nil { if err != nil {
println(err.Error()) handleError(err)
return return
} }
err = formatTemplate(w, "statistics", channelData) err = formatTemplate(w, "statistics", channelData)
if err != nil { if err != nil {
println(err.Error()) handleError(err)
return return
} }
} else { } else {
...@@ -181,12 +210,12 @@ func main() { ...@@ -181,12 +210,12 @@ func main() {
} }
} }
func retrievePercentageStats(db *sql.DB, stats string) ([]FloatEntry, error) { func retrievePercentageStats(db *sql.DB, channel int, stats string) ([]FloatEntry, error) {
var data []FloatEntry result, err := db.Query("SELECT coalesce(users.nick, '[Unknown]'), t."+stats+" FROM (SELECT coalesce(groups.\"group\", messages.sender) AS hash, round((count(nullif(messages."+stats+", false)) * 100) :: numeric / count(*)) as "+stats+" FROM messages LEFT JOIN groups ON messages.sender = groups.nick AND groups.channel = $1 WHERE messages.channel = $1 GROUP BY hash ORDER BY "+stats+" DESC) t LEFT JOIN users ON t.hash = users.hash WHERE t."+stats+" > 0 LIMIT $2;", channel, 2)
result, err := db.Query("SELECT coalesce(users.nick, '[Unknown]'), t." + stats + " FROM (SELECT coalesce(groups.\"group\", messages.sender) AS hash, round((count(nullif(messages." + stats + ", false)) * 100) :: numeric / count(*)) as " + stats + " FROM messages LEFT JOIN groups ON messages.sender = groups.nick AND groups.channel = 1 WHERE messages.channel = 1 GROUP BY hash ORDER BY " + stats + " DESC) t LEFT JOIN users ON t.hash = users.hash WHERE t." + stats + " > 0 LIMIT 2;")
if err != nil { if err != nil {
return nil, err return nil, err
} }
var data []FloatEntry
for result.Next() { for result.Next() {
var info FloatEntry var info FloatEntry
err := result.Scan(&info.Name, &info.Value) err := result.Scan(&info.Name, &info.Value)
...@@ -198,12 +227,12 @@ func retrievePercentageStats(db *sql.DB, stats string) ([]FloatEntry, error) { ...@@ -198,12 +227,12 @@ func retrievePercentageStats(db *sql.DB, stats string) ([]FloatEntry, error) {
return data, nil return data, nil
} }
func retrieveLongestLines(db *sql.DB) ([]FloatEntry, error) { func retrieveLongestLines(db *sql.DB, channel int) ([]FloatEntry, error) {
var data []FloatEntry result, err := db.Query("SELECT coalesce(users.nick, '[Unknown]'), t.average FROM (SELECT coalesce(groups.\"group\", messages.sender) AS hash, avg(messages.characters) as average FROM messages LEFT JOIN groups ON messages.sender = groups.nick AND groups.channel = $1 WHERE messages.channel = $1 GROUP BY hash ORDER BY average DESC) t LEFT JOIN users ON t.hash = users.hash LIMIT $2;", channel, 2)
result, err := db.Query("SELECT coalesce(users.nick, '[Unknown]'), t.average FROM (SELECT coalesce(groups.\"group\", messages.sender) AS hash, avg(messages.characters) as average FROM messages LEFT JOIN groups ON messages.sender = groups.nick AND groups.channel = 1 WHERE messages.channel = 1 GROUP BY hash ORDER BY average DESC) t LEFT JOIN users ON t.hash = users.hash LIMIT 2;")
if err != nil { if err != nil {
return nil, err return nil, err
} }
var data []FloatEntry
for result.Next() { for result.Next() {
var info FloatEntry var info FloatEntry
err := result.Scan(&info.Name, &info.Value) err := result.Scan(&info.Name, &info.Value)
...@@ -215,12 +244,12 @@ func retrieveLongestLines(db *sql.DB) ([]FloatEntry, error) { ...@@ -215,12 +244,12 @@ func retrieveLongestLines(db *sql.DB) ([]FloatEntry, error) {
return data, nil return data, nil
} }
func retrieveShortestLines(db *sql.DB) ([]FloatEntry, error) { func retrieveShortestLines(db *sql.DB, channel int) ([]FloatEntry, error) {
var data []FloatEntry result, err := db.Query("SELECT coalesce(users.nick, '[Unknown]'), t.average FROM (SELECT coalesce(groups.\"group\", messages.sender) AS hash, avg(messages.characters) as average FROM messages LEFT JOIN groups ON messages.sender = groups.nick AND groups.channel = $1 WHERE messages.channel = $1 GROUP BY hash ORDER BY average DESC) t LEFT JOIN users ON t.hash = users.hash LIMIT $2;", channel, 2)
result, err := db.Query("SELECT coalesce(users.nick, '[Unknown]'), t.average FROM (SELECT coalesce(groups.\"group\", messages.sender) AS hash, avg(messages.characters) as average FROM messages LEFT JOIN groups ON messages.sender = groups.nick AND groups.channel = 1 WHERE messages.channel = 1 GROUP BY hash ORDER BY average DESC) t LEFT JOIN users ON t.hash = users.hash LIMIT 2;")
if err != nil { if err != nil {
return nil, err return nil, err
} }
var data []FloatEntry
for result.Next() { for result.Next() {
var info FloatEntry var info FloatEntry
err := result.Scan(&info.Name, &info.Value) err := result.Scan(&info.Name, &info.Value)
...@@ -231,3 +260,57 @@ func retrieveShortestLines(db *sql.DB) ([]FloatEntry, error) { ...@@ -231,3 +260,57 @@ func retrieveShortestLines(db *sql.DB) ([]FloatEntry, error) {
} }
return data, nil return data, nil
} }
func retrieveTotalWords(db *sql.DB, channel int) ([]TotalEntry, error) {
result, err := db.Query("SELECT coalesce(users.nick, '[Unknown]'), t.words FROM (SELECT coalesce(groups.\"group\", messages.sender) AS hash, SUM(messages.words) as words FROM messages LEFT JOIN groups ON messages.sender = groups.nick AND groups.channel = $1 WHERE messages.channel = $1 GROUP BY hash ORDER BY words DESC) t LEFT JOIN users ON t.hash = users.hash LIMIT $2", channel, 2)
if err != nil {
return nil, err
}
var data []TotalEntry
previous := ""
for result.Next() {
var info TotalEntry
err := result.Scan(&info.Name, &info.Value)
if err != nil {
panic(err)
}
info.Previous = previous
previous = info.Name
data = append(data, info)
}
return data, nil
}
func retrieveUsers(db *sql.DB, channel int) ([]UserData, error) {
result, err := db.Query("SELECT coalesce(users.nick, '[Unknown]'), t.lines, t.words, t.wordsPerLine, t.lastSeen FROM (SELECT coalesce(groups.\"group\", messages.sender) AS hash, COUNT(*) as lines, SUM(messages.words) as words, AVG(messages.words) as wordsPerLine, MAX(messages.time) AS lastSeen FROM messages LEFT JOIN groups ON messages.sender = groups.nick AND groups.channel = $1 WHERE messages.channel = $1 GROUP BY hash ORDER BY lines DESC) t LEFT JOIN users ON t.hash = users.hash LIMIT $2", channel, 20)
if err != nil {
return nil, err
}
var data []UserData
for result.Next() {
var info UserData
err := result.Scan(&info.Name, &info.Lines, &info.Words, &info.WordsPerLine, &info.LastSeen)
if err != nil {
panic(err)
}
data = append(data, info)
}
return data, nil
}
func retrieveReferences(db *sql.DB, channel int) ([]ReferenceData, error) {
result, err := db.Query("SELECT coalesce(u1.nick, '[Unknown]') as target, t.count, coalesce(u2.nick, '[Unknown]') AS lastUsed FROM (SELECT coalesce(g1.\"group\", t.target) AS target, t.count, coalesce(g2.\"group\", \"references\".source) AS lastUsed FROM (SELECT \"references\".target AS target, COUNT(*) as count, MAX(id) AS lastUsed FROM \"references\" WHERE \"references\".channel = $1 GROUP BY target) t JOIN \"references\" ON t.lastUsed = \"references\".id LEFT JOIN groups g1 ON \"references\".source = g1.nick AND g1.channel = $1 LEFT JOIN groups g2 ON t.target = g2.nick AND g2.channel = $1) t LEFT JOIN users u1 ON t.target = u1.hash LEFT JOIN users u2 ON t.lastUsed = u2.hash ORDER BY count DESC LIMIT $2", channel, 5)
if err != nil {
return nil, err
}
var data []ReferenceData
for result.Next() {
var info ReferenceData
err := result.Scan(&info.Name, &info.Count, &info.LastUsed)
if err != nil {
panic(err)
}
data = append(data, info)
}
return data, nil
}
...@@ -20,18 +20,18 @@ ...@@ -20,18 +20,18 @@
<meta name="apple-mobile-web-app-status-bar-style" content="#FFC107"> <meta name="apple-mobile-web-app-status-bar-style" content="#FFC107">
<link href="/assets/css/style.css" rel="stylesheet"> <link href="/assets/css/style.css" rel="stylesheet">
<p>Total: {{.TotalCharacters}} Characters, {{.TotalWords}} Words, {{.TotalLines}} Lines</p> <p>Total: {{.Lines}} Lines, {{.Words}} Words, {{printf "%.1f" .WordsPerLine}} Words per Line</p>
<table> <table>
<thead> <thead>
<tr> <tr>
<th colspan="3">Most active nicks</th> <th colspan="5">Most active nicks</th>
</tr> </tr>
<tr> <tr>
<th>Nick</th> <th>Nick</th>
<th>Number of Characters</th>
<th>Number of Words</th>
<th>Number of Lines</th> <th>Number of Lines</th>
<th>Number of Words</th>
<th>Words per Line</th>
<th>Last seen</th> <th>Last seen</th>
</tr> </tr>
</thead> </thead>
...@@ -39,9 +39,9 @@ ...@@ -39,9 +39,9 @@
{{range .Users}} {{range .Users}}
<tr> <tr>
<td>{{.Name}}</td> <td>{{.Name}}</td>
<td>{{.Total}}</td>
<td>{{.Words}}</td>
<td>{{.Lines}}</td> <td>{{.Lines}}</td>
<td>{{.Words}}</td>
<td>{{printf "%.1f" .WordsPerLine}}</td>
<td>{{.LastSeen.Format "2006-01-02"}}</td> <td>{{.LastSeen.Format "2006-01-02"}}</td>
</tr> </tr>
{{end}} {{end}}
...@@ -131,6 +131,9 @@ ...@@ -131,6 +131,9 @@
</tr> </tr>
{{end}} {{end}}
{{end}} {{end}}
<tr>
<td>{{.Name}} average was {{printf "%.1f" .CharactersPerLine}} letters per line.</td>
</tr>
</tbody> </tbody>
<tbody> <tbody>
{{range $i, $e := .ShortestLines}} {{range $i, $e := .ShortestLines}}
...@@ -145,4 +148,53 @@ ...@@ -145,4 +148,53 @@
{{end}} {{end}}
{{end}} {{end}}
</tbody> </tbody>
<tbody>
{{range $i, $e := .TotalWords}}
{{with $e}}
<tr>
{{if eq $i 0}}
<td><b>{{.Name}}</b> spoke a total of {{.Value}} words!</td>
{{ else }}
<td>{{.Previous}}'s faithful follower, <b>{{.Name}}</b>, didn't speak so much: {{.Value}} words.</td>
{{end}}
</tr>
{{end}}
{{end}}
</tbody>
<tbody>
{{range $i, $e := .AverageWords}}
{{with $e}}
<tr>
{{if eq $i 0}}
<td><b>{{.Name}}</b> wrote an average of {{printf "%.1f" .Value}} words per line.</td>
{{end}}
</tr>
{{end}}
{{end}}
<tr>
<td>Channel average was {{printf "%.1f" .WordsPerLine}} words per line.</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th colspan="3">Most referenced nicks</th>
</tr>
<tr>
<th>Nick</th>
<th>Number of Uses</th>
<th>Last Used by</th>
</tr>
</thead>
<tbody>
{{range .References}}
<tr>
<td>{{.Name}}</td>
<td>{{.Count}}</td>
<td>{{.LastUsed}}</td>
</tr>
{{end}}
</tbody>
</table> </table>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment