Check for duplicate points

This commit is contained in:
Neale Pickett 2018-09-18 00:02:44 +00:00
parent 78f2e2a79c
commit 4c3fe34936
5 changed files with 93 additions and 52 deletions

View File

@ -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
}

View File

@ -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",

View File

@ -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 {

View File

@ -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)

View File

@ -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 (