Skip to content
Snippets Groups Projects
Commit 3e075433 authored by Ophestra's avatar Ophestra
Browse files

safe concurrent access, destroy team, export as CSV

parent 83335284
No related branches found
No related tags found
No related merge requests found
......@@ -23,6 +23,8 @@
</li>
<li>
<div class="my-match-info">
<a class="live-btn" type="submit" style="cursor: default"
href="/admin/{{.ID}}/csv">Export</a>
<form id="destroy-{{.ID}}" method="post" action="/admin/{{.ID}}">
<input type="hidden" name="Delete" value="true">
<a class="live-btn" type="submit" style="cursor: default"
......
......@@ -257,6 +257,36 @@ func registerRoutes() {
})
})
router.GET("/admin/:id/csv", func(context *gin.Context) {
user := oauth.GetSelf(context)
if user == nil {
context.Redirect(http.StatusTemporaryRedirect, "/auth/login")
return
}
if !administrators[user.ID] {
context.Redirect(http.StatusTemporaryRedirect, "/")
return
}
t := contextTournament(context)
if t == nil {
return
}
if c, err := csvTournament(t.ID); err != nil {
log.Printf("error rendering CSV for tournament %s: %s", t.ID.String(), err)
context.String(http.StatusInternalServerError, "Internal Server Error")
return
} else {
context.Writer.WriteHeader(http.StatusOK)
context.Header("Content-Disposition", "attachment; filename="+t.Attributes["title"]+".csv")
context.Header("Content-Type", "application/x-download")
context.Header("Content-Length", strconv.Itoa(len(c)))
_, _ = context.Writer.Write(c)
}
})
router.POST("/admin/:id", func(context *gin.Context) {
user := oauth.GetSelf(context)
if user == nil {
......
package main
import (
"bytes"
"encoding/csv"
"fmt"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
json "github.com/json-iterator/go"
......@@ -289,3 +292,56 @@ func validatePayloadTournament(payload *tournamentPayload) bool {
payload.StartTime().After(payload.Deadline()) &&
payload.Deadline().After(time.Now())
}
func csvTournament(t uuid.UUID) ([]byte, error) {
var captains []string
if cs, err := getTournamentTeam(t); err != nil {
return nil, err
} else {
captains = cs
}
if len(captains) == 0 {
return []byte{}, nil
}
teams := make([]*teamPayload, len(captains))
for i, captain := range captains {
if team, err := getTeam(t, captain); err != nil {
return nil, err
} else {
teams[i] = team
}
}
width := 5 + len(teams[0].Players)
records := make([][]string, len(teams)+1)
records[0] = make([]string, width)
records[0][0] = "ID"
records[0][1] = "Team Name"
records[0][2] = "Submitter Discord ID"
records[0][3] = "Submitter First Name"
records[0][4] = "Submitter Last Name"
for i := 3; i < width; i++ {
records[0][i] = fmt.Sprintf("Member %d", i-2)
}
for i, team := range teams {
records[i+1] = make([]string, width)
records[i+1][0] = strconv.Itoa(i + 1)
records[i+1][1] = team.Name
records[i+1][2] = team.Captain
records[i+1][3] = team.SubmitterFirstName
records[i+1][4] = team.SubmitterLastName
for j, player := range team.Players {
records[i+1][j+5] = player
}
}
buf := bytes.NewBuffer(nil)
if err := csv.NewWriter(buf).WriteAll(records); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
package main
import (
"fmt"
"github.com/bwmarrin/discordgo"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
......@@ -8,6 +9,7 @@ import (
"github.com/syndtr/goleveldb/leveldb"
"log"
"net/http"
"sync"
)
type userEnrollment struct {
......@@ -30,6 +32,8 @@ type teamPayload struct {
Tournament uuid.UUID `json:"tournament"`
}
var userLock = sync.RWMutex{}
func putTeam(t *teamPayload) error {
var b []byte
if p, err := getBytesTournament(t.Tournament); err != nil {
......@@ -38,6 +42,9 @@ func putTeam(t *teamPayload) error {
b = p
}
userLock.Lock()
defer userLock.Unlock()
var enrollments []string
if data, err := userDB.Get(b, nil); err != nil && !isNotFound(err) {
return err
......@@ -57,7 +64,7 @@ func putTeam(t *teamPayload) error {
}
}
var tournaments = make(map[string]*teamPayload)
var tournaments = make(map[uuid.UUID]*teamPayload)
if data, err := userDB.Get([]byte(t.Captain), nil); err != nil && !isNotFound(err) {
return err
} else {
......@@ -66,7 +73,7 @@ func putTeam(t *teamPayload) error {
return err
}
}
tournaments[string(b)] = t
tournaments[t.Tournament] = t
if data, err = json.Marshal(tournaments); err != nil {
return err
} else {
......@@ -79,6 +86,64 @@ func putTeam(t *teamPayload) error {
return nil
}
func destroyTeam(t uuid.UUID, captain string) error {
var b []byte
if p, err := getBytesTournament(t); err != nil {
return err
} else {
b = p
}
userLock.Lock()
defer userLock.Unlock()
var enrollments []string
if data, err := userDB.Get(b, nil); err != nil {
return err
} else {
if data != nil {
if err = json.Unmarshal(data, &enrollments); err != nil {
return err
}
}
for i, c := range enrollments {
if c == captain {
enrollments[i] = enrollments[len(enrollments)-1]
enrollments = enrollments[:len(enrollments)-1]
break
}
}
if data, err = json.Marshal(enrollments); err != nil {
return err
} else {
if err = userDB.Put(b, data, nil); err != nil {
return err
}
}
}
var tournaments = make(map[uuid.UUID]*teamPayload)
if data, err := userDB.Get([]byte(captain), nil); err != nil {
return err
} else {
if data != nil {
if err = json.Unmarshal(data, &tournaments); err != nil {
return err
}
}
delete(tournaments, t)
if data, err = json.Marshal(tournaments); err != nil {
return err
} else {
if err = userDB.Put([]byte(captain), data, nil); err != nil {
return err
}
}
}
return nil
}
func existsTeam(t uuid.UUID, captain string) (bool, error) {
if teams, err := getTournamentTeam(t); err != nil {
if isNotFound(err) {
......@@ -103,7 +168,7 @@ func getTeam(t uuid.UUID, captain string) (*teamPayload, error) {
b = p
}
var payload = make(map[string]*teamPayload)
var payload = make(map[uuid.UUID]*teamPayload)
if data, err := userDB.Get([]byte(captain), nil); err != nil && !isNotFound(err) {
return nil, err
} else {
......@@ -112,7 +177,9 @@ func getTeam(t uuid.UUID, captain string) (*teamPayload, error) {
return nil, err
}
}
if p, ok := payload[string(b)]; !ok {
if p, ok := payload[t]; !ok {
fmt.Println(payload)
fmt.Println(string(b))
return nil, leveldb.ErrNotFound
} else {
return p, nil
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment