mirror of https://github.com/dirtbags/moth.git
First pass on integrating PIDs in a more native way
This commit is contained in:
parent
b6eea388d9
commit
afb3cde38d
|
@ -125,6 +125,22 @@ func (h *HTTPServer) RegisterHandler(mh MothRequestHandler, w http.ResponseWrite
|
|||
}
|
||||
}
|
||||
|
||||
// AssignParticipantHandler handles attempts to associate a participant with a team
|
||||
func (h *HTTPServer) AssignParticipantHandler(mh MothRequestHandler, w http.ResponseWriter, req *http.Request) {
|
||||
if mh.participantID == "" {
|
||||
jsend.Sendf(w, jsend.Fail, "empty name", "Participant ID may not be empty")
|
||||
return
|
||||
}
|
||||
|
||||
if err := mh.AssignParticipant(); err != ErrAlreadyRegistered {
|
||||
jsend.Sendf(w, jsend.Success, "already assigned", "participant and team have already been associated")
|
||||
} else if err != nil {
|
||||
jsend.Sendf(w, jsend.Fail, "unable to associate participant and team", err.Error())
|
||||
} else {
|
||||
jsend.Sendf(w, jsend.Success, "assigned", "participant and team have been associated")
|
||||
}
|
||||
}
|
||||
|
||||
// AnswerHandler checks answer correctness and awards points
|
||||
func (h *HTTPServer) AnswerHandler(mh MothRequestHandler, w http.ResponseWriter, req *http.Request) {
|
||||
cat := req.FormValue("cat")
|
||||
|
|
|
@ -57,6 +57,7 @@ type StateProvider interface {
|
|||
PointsLog() award.List
|
||||
TeamName(teamID string) (string, error)
|
||||
SetTeamName(teamID, teamName string) error
|
||||
AssignParticipant(participantID string, teamID string) error
|
||||
AwardPoints(teamID string, cat string, points int) error
|
||||
LogEvent(event, participantID, teamID, cat string, points int, extra ...string)
|
||||
Maintainer
|
||||
|
@ -176,6 +177,20 @@ func (mh *MothRequestHandler) Register(teamName string) error {
|
|||
return mh.State.SetTeamName(mh.teamID, teamName)
|
||||
}
|
||||
|
||||
// AssignParticipant associates a participant with a team
|
||||
func (mh *MothRequestHandler) AssignParticipant() error {
|
||||
if mh.participantID == "" {
|
||||
return fmt.Errorf("empty participant ID")
|
||||
}
|
||||
|
||||
if mh.teamID == "" {
|
||||
return fmt.Errorf("empty participant ID")
|
||||
}
|
||||
|
||||
mh.State.LogEvent("assign", mh.participantID, mh.teamID, "", 0)
|
||||
return mh.State.AssignParticipant(mh.participantID, mh.teamID)
|
||||
}
|
||||
|
||||
// ExportState anonymizes team IDs and returns StateExport.
|
||||
// If a teamID has been specified for this MothRequestHandler,
|
||||
// the anonymized team name for this teamID has the special value "self".
|
||||
|
|
|
@ -46,6 +46,7 @@ type State struct {
|
|||
|
||||
// Caches, so we're not hammering NFS with metadata operations
|
||||
teamNames map[string]string
|
||||
participantTeams map[string]string
|
||||
pointsLog award.List
|
||||
messages string
|
||||
lock sync.RWMutex
|
||||
|
@ -60,6 +61,7 @@ func NewState(fs afero.Fs) *State {
|
|||
eventStream: make(chan []string, 80),
|
||||
|
||||
teamNames: make(map[string]string),
|
||||
participantTeams: make(map[string]string),
|
||||
}
|
||||
if err := s.reopenEventLog(); err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -175,6 +177,48 @@ func (s *State) SetTeamName(teamID, teamName string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// AssignParticipant associated a participant with a team
|
||||
// A participant can only be associated with one team at a time
|
||||
func (s *State) AssignParticipant(participantID string, teamID string) error {
|
||||
idsFile, err := s.Open("participantids.txt")
|
||||
if err != nil {
|
||||
return fmt.Errorf("participant IDs file does not exist")
|
||||
}
|
||||
defer idsFile.Close()
|
||||
found := false
|
||||
scanner := bufio.NewScanner(idsFile)
|
||||
for scanner.Scan() {
|
||||
if scanner.Text() == participantID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("participant ID not found in list of valid participant IDs")
|
||||
}
|
||||
|
||||
_, err = s.TeamName(teamID)
|
||||
|
||||
if (err != nil) {
|
||||
return fmt.Errorf("Provided team does not exist, or is not registered")
|
||||
}
|
||||
|
||||
teamParticipantFilename := filepath.Join("participants", participantID)
|
||||
teamParticipantFile, err := s.Fs.OpenFile(teamParticipantFilename, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0644)
|
||||
if os.IsExist(err) {
|
||||
return ErrAlreadyRegistered
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
defer teamParticipantFile.Close()
|
||||
log.Printf("Adding participant [%s] to team [%s]", participantID, teamID)
|
||||
fmt.Fprintln(teamParticipantFile, teamID)
|
||||
|
||||
s.refreshNow <- true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PointsLog retrieves the current points log.
|
||||
func (s *State) PointsLog() award.List {
|
||||
s.lock.RLock()
|
||||
|
@ -308,6 +352,7 @@ func (s *State) maybeInitialize() {
|
|||
s.RemoveAll("points.tmp")
|
||||
s.RemoveAll("points.new")
|
||||
s.RemoveAll("teams")
|
||||
s.RemoveAll("participants")
|
||||
|
||||
// Open log file
|
||||
if err := s.reopenEventLog(); err != nil {
|
||||
|
@ -319,6 +364,7 @@ func (s *State) maybeInitialize() {
|
|||
s.Mkdir("points.tmp", 0755)
|
||||
s.Mkdir("points.new", 0755)
|
||||
s.Mkdir("teams", 0755)
|
||||
s.Mkdir("participants", 0755)
|
||||
|
||||
// Preseed available team ids if file doesn't exist
|
||||
if f, err := s.OpenFile("teamids.txt", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644); err == nil {
|
||||
|
@ -333,6 +379,19 @@ func (s *State) maybeInitialize() {
|
|||
f.Close()
|
||||
}
|
||||
|
||||
// Preseed available participants if file doesn't exist
|
||||
if f, err := s.OpenFile("participantids.txt", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644); err == nil {
|
||||
id := make([]byte, 16)
|
||||
for i := 0; i < 100; i++ {
|
||||
for i := range id {
|
||||
char := rand.Intn(len(DistinguishableChars))
|
||||
id[i] = DistinguishableChars[char]
|
||||
}
|
||||
fmt.Fprintln(f, string(id))
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
|
||||
// Create some files
|
||||
if f, err := s.Create("initialized"); err == nil {
|
||||
fmt.Fprintln(f, "initialized: remove to re-initialize the contest.")
|
||||
|
@ -451,6 +510,28 @@ func (s *State) updateCaches() {
|
|||
|
||||
}
|
||||
|
||||
{
|
||||
// Update participant records
|
||||
for k := range s.participantTeams {
|
||||
delete(s.participantTeams, k)
|
||||
}
|
||||
|
||||
participantsFs := afero.NewBasePathFs(s.Fs, "participants")
|
||||
if dirents, err := afero.ReadDir(participantsFs, "."); err != nil {
|
||||
log.Printf("Reading participant ids: %v", err)
|
||||
} else {
|
||||
for _, dirent := range dirents {
|
||||
participantID := dirent.Name()
|
||||
if participantTeamBytes, err := afero.ReadFile(participantsFs, participantID); err != nil {
|
||||
log.Printf("Reading participant %s: %v", participantID, err)
|
||||
} else {
|
||||
teamID := strings.TrimSpace(string(participantTeamBytes))
|
||||
s.participantTeams[participantID] = teamID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if bMessages, err := afero.ReadFile(s, "messages.html"); err == nil {
|
||||
s.messages = string(bMessages)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue