2018-05-03 09:58:03 -06:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2018-05-04 17:20:51 -06:00
|
|
|
"bufio"
|
2018-05-03 09:58:03 -06:00
|
|
|
"fmt"
|
2018-05-04 17:20:51 -06:00
|
|
|
"io/ioutil"
|
2018-05-03 09:58:03 -06:00
|
|
|
"log"
|
|
|
|
"os"
|
2018-05-04 17:20:51 -06:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
2018-05-03 09:58:03 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
type Award struct {
|
2018-05-04 17:20:51 -06:00
|
|
|
when time.Time
|
2018-05-08 12:45:50 -06:00
|
|
|
teamid string
|
2018-05-04 17:20:51 -06:00
|
|
|
category string
|
|
|
|
points int
|
2018-05-03 09:58:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func ParseAward(s string) (*Award, error) {
|
|
|
|
ret := Award{}
|
|
|
|
|
|
|
|
parts := strings.SplitN(s, " ", 5)
|
|
|
|
if len(parts) < 4 {
|
2018-05-04 17:20:51 -06:00
|
|
|
return nil, fmt.Errorf("Malformed award string")
|
2018-05-03 09:58:03 -06:00
|
|
|
}
|
|
|
|
|
2018-05-04 17:20:51 -06:00
|
|
|
whenEpoch, err := strconv.ParseInt(parts[0], 10, 64)
|
2018-05-03 09:58:03 -06:00
|
|
|
if (err != nil) {
|
2018-05-04 17:20:51 -06:00
|
|
|
return nil, fmt.Errorf("Malformed timestamp: %s", parts[0])
|
2018-05-03 09:58:03 -06:00
|
|
|
}
|
|
|
|
ret.when = time.Unix(whenEpoch, 0)
|
|
|
|
|
2018-05-08 12:45:50 -06:00
|
|
|
ret.teamid = parts[1]
|
2018-05-03 09:58:03 -06:00
|
|
|
ret.category = parts[2]
|
|
|
|
|
2018-05-04 17:20:51 -06:00
|
|
|
points, err := strconv.Atoi(parts[3])
|
2018-05-03 09:58:03 -06:00
|
|
|
if (err != nil) {
|
2018-05-04 17:20:51 -06:00
|
|
|
return nil, fmt.Errorf("Malformed points: %s", parts[3])
|
2018-05-03 09:58:03 -06:00
|
|
|
}
|
2018-05-04 17:20:51 -06:00
|
|
|
ret.points = points
|
|
|
|
|
|
|
|
return &ret, nil
|
2018-05-03 09:58:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Award) String() string {
|
2018-05-08 12:45:50 -06:00
|
|
|
return fmt.Sprintf("%d %s %s %d", a.when.Unix(), a.teamid, a.category, a.points)
|
2018-05-04 17:20:51 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func pointsLog() []Award {
|
|
|
|
var ret []Award
|
|
|
|
|
|
|
|
fn := statePath("points.log")
|
|
|
|
f, err := os.Open(fn)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Unable to open %s: %s", fn, err)
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
|
|
for scanner.Scan() {
|
|
|
|
line := scanner.Text()
|
|
|
|
cur, err := ParseAward(line)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Skipping malformed award line %s: %s", line, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
ret = append(ret, *cur)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
2018-05-03 09:58:03 -06:00
|
|
|
}
|
|
|
|
|
2018-05-06 21:37:52 -06:00
|
|
|
// awardPoints gives points points to team teamid in category category
|
|
|
|
func awardPoints(teamid string, category string, points int) error {
|
|
|
|
fn := fmt.Sprintf("%s-%s-%d", teamid, category, points)
|
|
|
|
tmpfn := statePath("points.tmp", fn)
|
|
|
|
newfn := statePath("points.new", fn)
|
|
|
|
|
|
|
|
contents := fmt.Sprintf("%d %s %s %d\n", time.Now().Unix(), teamid, points)
|
|
|
|
|
|
|
|
if err := ioutil.WriteFile(tmpfn, []byte(contents), 0644); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.Rename(tmpfn, newfn); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-05-08 12:45:50 -06:00
|
|
|
log.Printf("Award %s %s %d", teamid, category, points)
|
2018-05-06 21:37:52 -06:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-05-03 09:58:03 -06:00
|
|
|
// collectPoints gathers up files in points.new/ and appends their contents to points.log,
|
|
|
|
// removing each points.new/ file as it goes.
|
|
|
|
func collectPoints() {
|
2018-05-04 17:20:51 -06:00
|
|
|
logf, err := os.OpenFile(statePath("points.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Can't append to points log: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer logf.Close()
|
2018-05-03 09:58:03 -06:00
|
|
|
|
2018-05-04 17:20:51 -06:00
|
|
|
files, err := ioutil.ReadDir(statePath("points.new"))
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Error reading packages: %s", err)
|
|
|
|
}
|
|
|
|
for _, f := range files {
|
2018-05-03 09:58:03 -06:00
|
|
|
filename := statePath("points.new", f.Name())
|
2018-05-04 17:20:51 -06:00
|
|
|
s, err := ioutil.ReadFile(filename)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Can't read points file %s: %s", filename, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
award, err := ParseAward(string(s))
|
|
|
|
if err != nil {
|
2018-05-03 09:58:03 -06:00
|
|
|
log.Printf("Can't parse award file %s: %s", filename, err)
|
|
|
|
continue
|
|
|
|
}
|
2018-05-04 17:20:51 -06:00
|
|
|
fmt.Fprintf(logf, "%s\n", award.String())
|
2018-05-03 09:58:03 -06:00
|
|
|
log.Print(award.String())
|
2018-05-04 17:20:51 -06:00
|
|
|
logf.Sync()
|
|
|
|
if err := os.Remove(filename); err != nil {
|
2018-05-03 09:58:03 -06:00
|
|
|
log.Printf("Unable to remove %s: %s", filename, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|