mirror of https://github.com/dirtbags/moth.git
Check for duplicate points
This commit is contained in:
parent
8b1441a591
commit
f914f1d65d
46
src/award.go
46
src/award.go
|
@ -14,6 +14,25 @@ type Award struct {
|
|||
Points int
|
||||
}
|
||||
|
||||
func ParseAward(s string) (*Award, error) {
|
||||
ret := Award{}
|
||||
|
||||
s = strings.Trim(s, " \t\n")
|
||||
|
||||
var whenEpoch int64
|
||||
|
||||
n, err := fmt.Sscanf(s, "%d %s %s %d", &whenEpoch, &ret.TeamId, &ret.Category, &ret.Points)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if n != 4 {
|
||||
return nil, fmt.Errorf("Malformed award string: only parsed %d fields", n)
|
||||
}
|
||||
|
||||
ret.When = time.Unix(whenEpoch, 0)
|
||||
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
func (a *Award) String() string {
|
||||
return fmt.Sprintf("%d %s %s %d", a.When.Unix(), a.TeamId, a.Category, a.Points)
|
||||
}
|
||||
|
@ -40,21 +59,14 @@ func (a *Award) MarshalJSON() ([]byte, error) {
|
|||
return []byte(ret), nil
|
||||
}
|
||||
|
||||
func ParseAward(s string) (*Award, error) {
|
||||
ret := Award{}
|
||||
|
||||
s = strings.Trim(s, " \t\n")
|
||||
|
||||
var whenEpoch int64
|
||||
|
||||
n, err := fmt.Sscanf(s, "%d %s %s %d", &whenEpoch, &ret.TeamId, &ret.Category, &ret.Points)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if n != 4 {
|
||||
return nil, fmt.Errorf("Malformed award string: only parsed %d fields", n)
|
||||
func (a *Award) Same(o *Award) bool {
|
||||
switch {
|
||||
case a.TeamId != o.TeamId:
|
||||
return false
|
||||
case a.Category != o.Category:
|
||||
return false
|
||||
case a.Points != o.Points:
|
||||
return false
|
||||
}
|
||||
|
||||
ret.When = time.Unix(whenEpoch, 0)
|
||||
|
||||
return &ret, nil
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -22,6 +23,32 @@ func respond(w http.ResponseWriter, req *http.Request, status Status, short stri
|
|||
}
|
||||
}
|
||||
|
||||
// anchoredSearch looks for needle in r,
|
||||
// skipping the first skip space-delimited words
|
||||
func anchoredSearch(r io.Reader, needle string, skip int) bool {
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.SplitN(line, " ", skip+1)
|
||||
if (len(parts) > skip) && (parts[skip] == needle) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// anchoredSearchFile performs an anchoredSearch on a given filename
|
||||
func anchoredSearchFile(filename string, needle string, skip int) bool {
|
||||
r, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
return anchoredSearch(r, needle, skip)
|
||||
}
|
||||
|
||||
func (ctx Instance) registerHandler(w http.ResponseWriter, req *http.Request) {
|
||||
teamname := req.FormValue("name")
|
||||
teamid := req.FormValue("id")
|
||||
|
@ -172,7 +199,7 @@ func (ctx Instance) answerHandler(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if err := ctx.AwardPoints(teamid, category, points); err != nil {
|
||||
if err := ctx.AwardPointsUniquely(teamid, category, points); err != nil {
|
||||
respond(
|
||||
w, req, Error,
|
||||
"Error awarding points",
|
||||
|
|
|
@ -89,8 +89,8 @@ func (ctx *Instance) StatePath(parts ...string) string {
|
|||
return path.Join(ctx.StateDir, tail)
|
||||
}
|
||||
|
||||
func (ctx *Instance) PointsLog() []Award {
|
||||
var ret []Award
|
||||
func (ctx *Instance) PointsLog() []*Award {
|
||||
var ret []*Award
|
||||
|
||||
fn := ctx.StatePath("points.log")
|
||||
f, err := os.Open(fn)
|
||||
|
@ -108,14 +108,14 @@ func (ctx *Instance) PointsLog() []Award {
|
|||
log.Printf("Skipping malformed award line %s: %s", line, err)
|
||||
continue
|
||||
}
|
||||
ret = append(ret, *cur)
|
||||
ret = append(ret, cur)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// awardPoints gives points points to team teamid in category category
|
||||
func (ctx *Instance) AwardPoints(teamid string, category string, points int) error {
|
||||
func (ctx *Instance) AwardPoints(teamid, category string, points int) error {
|
||||
fn := fmt.Sprintf("%s-%s-%d", teamid, category, points)
|
||||
tmpfn := ctx.StatePath("points.tmp", fn)
|
||||
newfn := ctx.StatePath("points.new", fn)
|
||||
|
@ -134,6 +134,23 @@ func (ctx *Instance) AwardPoints(teamid string, category string, points int) err
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ctx *Instance) AwardPointsUniquely(teamid, category string, points int) error {
|
||||
a := Award{
|
||||
When: time.Now(),
|
||||
TeamId: teamid,
|
||||
Category: category,
|
||||
Points: points,
|
||||
}
|
||||
|
||||
for _, e := range ctx.PointsLog() {
|
||||
if a.Same(e) {
|
||||
return fmt.Errorf("Points already awarded to this team in this category")
|
||||
}
|
||||
}
|
||||
|
||||
return ctx.AwardPoints(teamid, category, points)
|
||||
}
|
||||
|
||||
func (ctx *Instance) OpenCategoryFile(category string, parts ...string) (io.ReadCloser, error) {
|
||||
mb, ok := ctx.Categories[category]
|
||||
if !ok {
|
||||
|
|
|
@ -90,8 +90,21 @@ func (ctx *Instance) CollectPoints() {
|
|||
log.Printf("Can't parse award file %s: %s", filename, err)
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(logf, "%s\n", award.String())
|
||||
log.Print("XXX: check for duplicates", award.String())
|
||||
|
||||
duplicate := false
|
||||
for _, e := range ctx.PointsLog() {
|
||||
if award.Same(e) {
|
||||
duplicate = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if duplicate {
|
||||
log.Printf("Skipping duplicate points: %s", award.String())
|
||||
} else {
|
||||
fmt.Fprintf(logf, "%s\n", award.String())
|
||||
}
|
||||
|
||||
logf.Sync()
|
||||
if err := os.Remove(filename); err != nil {
|
||||
log.Printf("Unable to remove %s: %s", filename, err)
|
||||
|
|
|
@ -1,42 +1,14 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// anchoredSearch looks for needle in r,
|
||||
// skipping the first skip space-delimited words
|
||||
func anchoredSearch(r io.Reader, needle string, skip int) bool {
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.SplitN(line, " ", skip+1)
|
||||
if (len(parts) > skip) && (parts[skip] == needle) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// anchoredSearchFile performs an anchoredSearch on a given filename
|
||||
func anchoredSearchFile(filename string, needle string, skip int) bool {
|
||||
r, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
return anchoredSearch(r, needle, skip)
|
||||
}
|
||||
|
||||
type Status int
|
||||
|
||||
const (
|
||||
|
|
Loading…
Reference in New Issue