1
0
mirror of https://github.com/thebaer/tildes.git synced 2018-07-20 07:15:21 +00:00
tildes/tildescores/scores.go

341 lines
7.4 KiB
Go
Raw Normal View History

package main
import (
2015-02-24 00:06:28 +00:00
"bufio"
"flag"
"fmt"
"io/ioutil"
"os"
2015-02-01 04:41:11 +00:00
"os/exec"
"sort"
"strconv"
"strings"
"text/template"
2015-02-24 00:06:28 +00:00
"time"
"github.com/thebaer/tildes/store"
)
2015-02-01 02:40:26 +00:00
var (
2015-02-24 00:06:28 +00:00
scoresPath = "/home/krowbar/Code/irc/tildescores.txt"
jackpotPath = "/home/krowbar/Code/irc/tildejackpot.txt"
2015-02-01 04:41:11 +00:00
addictionData = "/home/karlen/bin/tilderoyale"
2015-02-01 02:40:26 +00:00
)
const (
scoreDeltasPath = "/home/bear/scoredeltas.txt"
2015-02-24 00:06:28 +00:00
deltaDelimiter = "+++"
)
func main() {
fmt.Println("Starting...")
// Get any arguments
outPtr := flag.String("o", "tildescores", "Output file name")
2015-02-01 02:40:26 +00:00
isTestPtr := flag.Bool("t", false, "Specifies we're developing")
flag.Parse()
2015-02-01 02:40:26 +00:00
if *isTestPtr {
scoresPath = "/home/bear/tildescores.txt"
2015-02-11 02:59:03 +00:00
jackpotPath = "/home/bear/tildejackpot.txt"
2015-02-01 04:41:11 +00:00
addictionData = "/home/bear/addicted.sh"
2015-02-01 02:40:26 +00:00
}
2015-02-24 00:06:28 +00:00
headers := []string{"User", "Tildes", "Last Collected", "Addiction", "# Asks", "Avg.", "Last Amt."}
scoresData := store.ReadRows(scoresPath, "&^%")
updatesData := store.ReadRows(scoreDeltasPath, deltaDelimiter)
scoresData = checkScoreDelta(scoresData, updatesData)
scoresTable := buildScoresTable(scoresData, headers)
2015-02-11 02:59:24 +00:00
generate("!tilde scores", getFile(jackpotPath), sortScore(scoresTable), *outPtr)
}
2015-02-18 03:43:59 +00:00
type table struct {
Headers []string
2015-02-24 00:06:28 +00:00
Rows []store.Row
}
type By func(r1, r2 *store.Row) bool
2015-02-24 00:06:28 +00:00
func (by By) Sort(rows []store.Row) {
2015-02-24 00:06:28 +00:00
rs := &rowSorter{
rows: rows,
2015-02-24 00:06:28 +00:00
by: by,
}
sort.Sort(rs)
}
2015-02-24 00:06:28 +00:00
type rowSorter struct {
rows []store.Row
2015-02-24 00:06:28 +00:00
by func(r1, r2 *store.Row) bool
}
2015-02-24 00:06:28 +00:00
func (r *rowSorter) Len() int {
return len(r.rows)
}
func (r *rowSorter) Swap(i, j int) {
r.rows[i], r.rows[j] = r.rows[j], r.rows[i]
}
func (r *rowSorter) Less(i, j int) bool {
return r.by(&r.rows[i], &r.rows[j])
}
2015-02-18 03:43:59 +00:00
func sortScore(table *table) *table {
score := func(r1, r2 *store.Row) bool {
s1, _ := strconv.Atoi(r1.Data[1])
s2, _ := strconv.Atoi(r2.Data[1])
return s1 < s2
}
decScore := func(r1, r2 *store.Row) bool {
return !score(r1, r2)
}
By(decScore).Sort(table.Rows)
return table
}
2015-01-27 21:17:46 +00:00
func parseTimestamp(ts string) time.Time {
t, err := strconv.ParseInt(ts, 10, 64)
if err != nil {
panic(err)
}
return time.Unix(t, 0)
}
2015-02-01 05:07:10 +00:00
func trimTrailingZeros(n float64) string {
return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.2f", n), "0"), ".")
}
func trimTrailingZerosShort(n float64) string {
return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.1f", n), "0"), ".")
}
func niceTime(sec int) string {
if sec == 0 {
return "-"
}
if sec < 60 {
return fmt.Sprintf("%dsec", sec)
} else if sec < 3600 {
2015-02-24 00:06:28 +00:00
return fmt.Sprintf("%smin", trimTrailingZerosShort(float64(sec)/60.0))
2015-02-01 05:07:10 +00:00
} else if sec < 86400 {
2015-02-24 00:06:28 +00:00
return fmt.Sprintf("%shr", trimTrailingZerosShort(float64(sec)/3600.0))
2015-02-01 05:07:10 +00:00
}
2015-02-24 00:06:28 +00:00
return fmt.Sprintf("%sdy", trimTrailingZerosShort(float64(sec)/86400.0))
2015-02-01 05:07:10 +00:00
}
type LastScore struct {
2015-02-24 00:06:28 +00:00
LastUpdate int
LastScore int
LastIncrement int
2015-02-24 00:06:28 +00:00
Times int
ScoreOffset int
Addiction int
}
func checkScoreDelta(scoreRows, deltaRows *[]store.Row) *[]store.Row {
users := make(map[string]LastScore)
// Read score delta data
for i := range *deltaRows {
r := (*deltaRows)[i]
if len(r.Data) < 4 {
break
}
score, _ := strconv.Atoi(r.Data[1])
update, _ := strconv.Atoi(r.Data[2])
inc, _ := strconv.Atoi(r.Data[3])
times, _ := strconv.Atoi(r.Data[4])
so, _ := strconv.Atoi(r.Data[5])
2015-02-24 00:06:28 +00:00
users[r.Data[0]] = LastScore{LastScore: score, LastUpdate: update, LastIncrement: inc, Times: times, ScoreOffset: so}
}
2015-02-01 04:41:11 +00:00
// Fetch IRC log data
fmt.Println("Fetching IRC log data")
cmd := exec.Command(addictionData, "/home/karlen/bin/addictedtotilde -c")
stdout, err := cmd.StdoutPipe()
if err != nil {
fmt.Println(err)
}
if err := cmd.Start(); err != nil {
fmt.Println(err)
}
r := bufio.NewReader(stdout)
scanner := bufio.NewScanner(r)
for scanner.Scan() {
logRow := strings.Split(scanner.Text(), "\t")
2015-02-01 05:07:10 +00:00
2015-02-01 04:41:11 +00:00
uname := strings.TrimSpace(logRow[0])
asks, _ := strconv.Atoi(strings.TrimSpace(logRow[3]))
2015-02-01 05:07:10 +00:00
addiction, _ := strconv.Atoi(strings.TrimSpace(logRow[1]))
u, exists := users[uname]
2015-02-01 04:41:11 +00:00
if !exists {
2015-02-24 00:06:28 +00:00
u = LastScore{ScoreOffset: 0}
2015-02-01 04:41:11 +00:00
}
u.ScoreOffset = 0
u.Times = asks
2015-02-01 05:07:10 +00:00
u.Addiction = addiction
fmt.Println(fmt.Sprintf("%d", u.Times))
2015-02-01 04:41:11 +00:00
users[uname] = u
}
2015-02-01 05:07:10 +00:00
// Add in scores data
fmt.Println(fmt.Sprintf("Reading scores data on %d users", len(*scoreRows)))
for i := range *scoreRows {
r := (*scoreRows)[i]
u, exists := users[r.Data[0]]
2015-02-01 05:07:10 +00:00
fmt.Println(u)
score, _ := strconv.Atoi(r.Data[1])
update, _ := strconv.Atoi(r.Data[2])
// Fill in any missing users
if !exists {
2015-02-24 00:06:28 +00:00
u = LastScore{LastScore: score, LastIncrement: -1, LastUpdate: update, Times: 0, ScoreOffset: score, Addiction: 0}
users[r.Data[0]] = u
}
// Match up "last collection" with rest of table data
if update > u.LastUpdate {
u.LastIncrement = score - u.LastScore
u.LastUpdate = update
u.LastScore = score
u.Times++
}
2015-02-01 05:07:10 +00:00
r.Data = append(r.Data, niceTime(u.Addiction))
2015-02-24 00:06:28 +00:00
var asksStr string
if u.Times > 0 {
asksStr = strconv.Itoa(u.Times)
} else {
asksStr = "-"
}
r.Data = append(r.Data, asksStr)
var avgStr string
if u.Times > 0 {
2015-02-24 00:06:28 +00:00
avg := float64(score-u.ScoreOffset) / float64(u.Times)
2015-02-01 05:07:10 +00:00
avgStr = trimTrailingZeros(avg)
} else {
avgStr = "-"
}
r.Data = append(r.Data, avgStr)
2015-02-01 02:42:33 +00:00
var lastIncStr string
if u.LastIncrement > -1 {
lastIncStr = strconv.Itoa(u.LastIncrement)
} else {
lastIncStr = "-"
}
r.Data = append(r.Data, lastIncStr)
users[r.Data[0]] = u
(*scoreRows)[i] = r
}
// Write deltas
2015-02-24 00:06:28 +00:00
f, err := os.OpenFile(scoreDeltasPath, os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
fmt.Println(err)
}
defer f.Close()
for k, v := range users {
2015-02-01 02:45:36 +00:00
userData := fmt.Sprintf("%d%s%d%s%d%s%d%s%d", v.LastScore, deltaDelimiter, v.LastUpdate, deltaDelimiter, v.LastIncrement, deltaDelimiter, v.Times, deltaDelimiter, v.ScoreOffset)
_, err = f.WriteString(fmt.Sprintf("%s%s%s\n", k, deltaDelimiter, userData))
if err != nil {
fmt.Println(err)
}
}
return scoreRows
}
2015-02-18 03:43:59 +00:00
func buildScoresTable(rows *[]store.Row, headers []string) *table {
t := &table{Headers: headers, Rows: nil}
const layout = "Jan 2, 2006 3:04pm MST"
for i, r := range *rows {
data := r.Data
time := parseTimestamp(r.Data[2])
r.Data[2] = time.UTC().Format(layout)
outRow := &store.Row{Data: data}
(*rows)[i] = *outRow
}
t.Rows = *rows
return t
}
2015-02-11 02:59:03 +00:00
func getFile(path string) string {
f, err := ioutil.ReadFile(path)
if err != nil {
fmt.Println(err)
return ""
}
return string(f)
}
type Page struct {
2015-02-24 00:06:28 +00:00
Title string
Table table
Updated string
UpdatedForHumans string
2015-02-24 00:06:28 +00:00
Jackpot int
}
2015-01-27 21:18:16 +00:00
func add(x, y int) int {
return x + y
}
2015-02-18 03:43:59 +00:00
func generate(title, jackpot string, table *table, outputFile string) {
fmt.Println("Generating page.")
f, err := os.Create(os.Getenv("HOME") + "/public_html/" + outputFile + ".html")
if err != nil {
panic(err)
}
2015-02-24 00:06:28 +00:00
defer f.Close()
2015-02-24 00:06:28 +00:00
funcMap := template.FuncMap{
2015-01-27 21:18:16 +00:00
"add": add,
}
w := bufio.NewWriter(f)
2015-02-11 02:39:47 +00:00
template, err := template.New("").Funcs(funcMap).ParseFiles("../templates/scores.html")
if err != nil {
panic(err)
}
// Extra page data
curTime := time.Now().UTC()
updatedReadable := curTime.Format(time.RFC1123)
updated := curTime.Format(time.RFC3339)
2015-02-11 02:59:03 +00:00
// Jackpot parsing
jp, err := strconv.Atoi(jackpot)
if err != nil {
fmt.Println(err)
jp = -1
}
// Generate the page
2015-02-11 02:59:03 +00:00
page := &Page{Title: title, Table: *table, UpdatedForHumans: updatedReadable, Updated: updated, Jackpot: jp}
template.ExecuteTemplate(w, "table", page)
w.Flush()
fmt.Println("DONE!")
}