diff --git a/main.go b/main.go index 6ce22377c11faff95b03fbeab344eae152270646..dfa5fdcb8f4eb53bdf4039de2137813d01f08b38 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,8 @@ import ( "time" ) +const DEBUG = true + type Config struct { Database DatabaseConfig } @@ -45,21 +47,23 @@ func formatTemplate(w http.ResponseWriter, templateName string, data interface{} } type ChannelData struct { - Id int - Name string - TotalCharacters int - TotalWords int - TotalLines int - Users []UserData - Questions []FloatEntry - Exclamations []FloatEntry - Caps []FloatEntry - EmojiHappy []FloatEntry - EmojiSad []FloatEntry - LongestLines []FloatEntry - ShortestLines []FloatEntry - Total []IntEntry - Average float64 + Id int + Name string + Lines int + Words int + WordsPerLine float64 + CharactersPerLine float64 + Users []UserData + Questions []FloatEntry + Exclamations []FloatEntry + Caps []FloatEntry + EmojiHappy []FloatEntry + EmojiSad []FloatEntry + LongestLines []FloatEntry + ShortestLines []FloatEntry + TotalWords []TotalEntry + AverageWords []FloatEntry + References []ReferenceData } type FloatEntry struct { @@ -72,12 +76,32 @@ type IntEntry struct { Value int } +type TotalEntry struct { + Name string + Value int + Previous string +} + type UserData struct { + Name string + Lines int + Words int + WordsPerLine float64 + LastSeen time.Time +} + +type ReferenceData struct { Name string - Total int - Words int - Lines int - LastSeen time.Time + LastUsed string + Count string +} + +func handleError(err error) { + if DEBUG { + panic(err) + } else { + handleError(err) + } } func main() { @@ -95,73 +119,78 @@ func main() { channelData := ChannelData{} err = db.QueryRow("SELECT id, channel FROM channels WHERE channel ILIKE $1", channel).Scan(&channelData.Id, &channelData.Name) if err != nil { - println(err.Error()) + handleError(err) 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 { - println(err.Error()) + handleError(err) 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 { - println(err.Error()) + handleError(err) return } - for result.Next() { - var info UserData - err := result.Scan(&info.Name, &info.Total, &info.Words, &info.Lines, &info.LastSeen) - if err != nil { - panic(err) - } - channelData.Users = append(channelData.Users, info) + + channelData.Questions, err = retrievePercentageStats(db, channelData.Id, "question") + if err != nil { + handleError(err) + return } - channelData.Questions, err = retrievePercentageStats(db, "question") + channelData.Exclamations, err = retrievePercentageStats(db, channelData.Id, "exclamation") if err != nil { - println(err.Error()) + handleError(err) return } - channelData.Exclamations, err = retrievePercentageStats(db, "exclamation") + channelData.Caps, err = retrievePercentageStats(db, channelData.Id, "caps") if err != nil { - println(err.Error()) + handleError(err) return } - channelData.Caps, err = retrievePercentageStats(db, "caps") + channelData.EmojiHappy, err = retrievePercentageStats(db, channelData.Id, "emoji_happy") if err != nil { - println(err.Error()) + handleError(err) return } - channelData.EmojiHappy, err = retrievePercentageStats(db, "emoji_happy") + channelData.EmojiSad, err = retrievePercentageStats(db, channelData.Id, "emoji_sad") if err != nil { - println(err.Error()) + handleError(err) return } - channelData.EmojiSad, err = retrievePercentageStats(db, "emoji_sad") + channelData.LongestLines, err = retrieveLongestLines(db, channelData.Id) if err != nil { - println(err.Error()) + handleError(err) return } - channelData.LongestLines, err = retrieveLongestLines(db) + channelData.ShortestLines, err = retrieveShortestLines(db, channelData.Id) if err != nil { - println(err.Error()) + handleError(err) return } - channelData.ShortestLines, err = retrieveShortestLines(db) + channelData.TotalWords, err = retrieveTotalWords(db, channelData.Id) if err != nil { - println(err.Error()) + handleError(err) + return + } + + channelData.References, err = retrieveReferences(db, channelData.Id) + if err != nil { + handleError(err) return } err = formatTemplate(w, "statistics", channelData) if err != nil { - println(err.Error()) + handleError(err) return } } else { @@ -181,12 +210,12 @@ func main() { } } -func retrievePercentageStats(db *sql.DB, 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;") +func retrievePercentageStats(db *sql.DB, channel int, stats string) ([]FloatEntry, error) { + 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) if err != nil { return nil, err } + var data []FloatEntry for result.Next() { var info FloatEntry err := result.Scan(&info.Name, &info.Value) @@ -198,12 +227,12 @@ func retrievePercentageStats(db *sql.DB, stats string) ([]FloatEntry, error) { return data, nil } -func retrieveLongestLines(db *sql.DB) ([]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;") +func retrieveLongestLines(db *sql.DB, channel int) ([]FloatEntry, error) { + 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) if err != nil { return nil, err } + var data []FloatEntry for result.Next() { var info FloatEntry err := result.Scan(&info.Name, &info.Value) @@ -215,12 +244,12 @@ func retrieveLongestLines(db *sql.DB) ([]FloatEntry, error) { return data, nil } -func retrieveShortestLines(db *sql.DB) ([]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;") +func retrieveShortestLines(db *sql.DB, channel int) ([]FloatEntry, error) { + 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) if err != nil { return nil, err } + var data []FloatEntry for result.Next() { var info FloatEntry err := result.Scan(&info.Name, &info.Value) @@ -231,3 +260,57 @@ func retrieveShortestLines(db *sql.DB) ([]FloatEntry, error) { } 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 +} diff --git a/templates/statistics.html b/templates/statistics.html index d6ef8936103e5421bafd8a07a98ea6564a91953b..a4945aaeeb281b7b46104b6f58dcc52714acaf1b 100644 --- a/templates/statistics.html +++ b/templates/statistics.html @@ -20,18 +20,18 @@ <meta name="apple-mobile-web-app-status-bar-style" content="#FFC107"> <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> <thead> <tr> - <th colspan="3">Most active nicks</th> + <th colspan="5">Most active nicks</th> </tr> <tr> <th>Nick</th> - <th>Number of Characters</th> - <th>Number of Words</th> <th>Number of Lines</th> + <th>Number of Words</th> + <th>Words per Line</th> <th>Last seen</th> </tr> </thead> @@ -39,9 +39,9 @@ {{range .Users}} <tr> <td>{{.Name}}</td> - <td>{{.Total}}</td> - <td>{{.Words}}</td> <td>{{.Lines}}</td> + <td>{{.Words}}</td> + <td>{{printf "%.1f" .WordsPerLine}}</td> <td>{{.LastSeen.Format "2006-01-02"}}</td> </tr> {{end}} @@ -131,6 +131,9 @@ </tr> {{end}} {{end}} + <tr> + <td>{{.Name}} average was {{printf "%.1f" .CharactersPerLine}} letters per line.</td> + </tr> </tbody> <tbody> {{range $i, $e := .ShortestLines}} @@ -145,4 +148,53 @@ {{end}} {{end}} </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> \ No newline at end of file