Implement bracket audience display for double-elimination.

This commit is contained in:
Patrick Fairbank
2022-08-20 16:01:54 -07:00
parent e63fee3cbc
commit 5426d56de5
16 changed files with 680 additions and 104 deletions

View File

@@ -34,6 +34,19 @@ type RankingWithNickname struct {
Nickname string
}
type allianceMatchup struct {
Round int
Group int
DisplayName string
RedAllianceSource string
BlueAllianceSource string
RedAlliance *model.Alliance
BlueAlliance *model.Alliance
IsActive bool
SeriesLeader string
SeriesStatus string
}
// Generates a JSON dump of the matches and results.
func (web *Web) matchesApiHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
@@ -211,3 +224,61 @@ func (web *Web) teamAvatarsApiHandler(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, avatarPath)
}
func (web *Web) bracketSvgApiHandler(w http.ResponseWriter, r *http.Request) {
alliances, err := web.arena.Database.GetAllAlliances()
if err != nil {
handleWebErr(w, err)
return
}
activeMatch := web.arena.SavedMatch
matchups := make(map[string]*allianceMatchup)
if web.arena.PlayoffBracket != nil {
for _, matchup := range web.arena.PlayoffBracket.GetAllMatchups() {
allianceMatchup := allianceMatchup{
Round: matchup.Round,
Group: matchup.Group,
DisplayName: matchup.LongDisplayName(),
RedAllianceSource: matchup.RedAllianceSourceDisplayName(),
BlueAllianceSource: matchup.BlueAllianceSourceDisplayName(),
}
if matchup.RedAllianceId > 0 {
if len(alliances) > 0 {
allianceMatchup.RedAlliance = &alliances[matchup.RedAllianceId-1]
} else {
allianceMatchup.RedAlliance = &model.Alliance{Id: matchup.RedAllianceId}
}
}
if matchup.BlueAllianceId > 0 {
if len(alliances) > 0 {
allianceMatchup.BlueAlliance = &alliances[matchup.BlueAllianceId-1]
} else {
allianceMatchup.BlueAlliance = &model.Alliance{Id: matchup.BlueAllianceId}
}
}
if activeMatch != nil {
allianceMatchup.IsActive = activeMatch.ElimRound == matchup.Round &&
activeMatch.ElimGroup == matchup.Group
}
allianceMatchup.SeriesLeader, allianceMatchup.SeriesStatus = matchup.StatusText()
matchups[fmt.Sprintf("%d_%d", matchup.Round, matchup.Group)] = &allianceMatchup
}
}
template, err := web.parseFiles("templates/bracket.svg")
if err != nil {
handleWebErr(w, err)
return
}
data := struct {
*model.EventSettings
Matchups map[string]*allianceMatchup
}{web.arena.EventSettings, matchups}
w.Header().Set("Content-Type", "image/svg+xml")
err = template.ExecuteTemplate(w, "bracket", data)
if err != nil {
handleWebErr(w, err)
return
}
}

View File

@@ -7,6 +7,7 @@ import (
"encoding/json"
"github.com/Team254/cheesy-arena-lite/game"
"github.com/Team254/cheesy-arena-lite/model"
"github.com/Team254/cheesy-arena-lite/tournament"
"github.com/Team254/cheesy-arena-lite/websocket"
gorillawebsocket "github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
@@ -31,7 +32,7 @@ func TestMatchesApi(t *testing.T) {
recorder := web.getHttpResponse("/api/matches/qualification")
assert.Equal(t, 200, recorder.Code)
assert.Equal(t, "application/json", recorder.HeaderMap["Content-Type"][0])
assert.Equal(t, "application/json", recorder.Header()["Content-Type"][0])
var matchesData []MatchWithResult
err := json.Unmarshal([]byte(recorder.Body.String()), &matchesData)
assert.Nil(t, err)
@@ -49,7 +50,7 @@ func TestRankingsApi(t *testing.T) {
// Test that empty rankings produces an empty array.
recorder := web.getHttpResponse("/api/rankings")
assert.Equal(t, 200, recorder.Code)
assert.Equal(t, "application/json", recorder.HeaderMap["Content-Type"][0])
assert.Equal(t, "application/json", recorder.Header()["Content-Type"][0])
rankingsData := struct {
Rankings []RankingWithNickname
TeamNicknames map[string]string
@@ -71,7 +72,7 @@ func TestRankingsApi(t *testing.T) {
recorder = web.getHttpResponse("/api/rankings")
assert.Equal(t, 200, recorder.Code)
assert.Equal(t, "application/json", recorder.HeaderMap["Content-Type"][0])
assert.Equal(t, "application/json", recorder.Header()["Content-Type"][0])
err = json.Unmarshal([]byte(recorder.Body.String()), &rankingsData)
assert.Nil(t, err)
if assert.Equal(t, 2, len(rankingsData.Rankings)) {
@@ -91,7 +92,7 @@ func TestSponsorSlidesApi(t *testing.T) {
recorder := web.getHttpResponse("/api/sponsor_slides")
assert.Equal(t, 200, recorder.Code)
assert.Equal(t, "application/json", recorder.HeaderMap["Content-Type"][0])
assert.Equal(t, "application/json", recorder.Header()["Content-Type"][0])
var sponsorSlides []model.SponsorSlide
err := json.Unmarshal([]byte(recorder.Body.String()), &sponsorSlides)
assert.Nil(t, err)
@@ -108,7 +109,7 @@ func TestAlliancesApi(t *testing.T) {
recorder := web.getHttpResponse("/api/alliances")
assert.Equal(t, 200, recorder.Code)
assert.Equal(t, "application/json", recorder.HeaderMap["Content-Type"][0])
assert.Equal(t, "application/json", recorder.Header()["Content-Type"][0])
var alliances []model.Alliance
err := json.Unmarshal([]byte(recorder.Body.String()), &alliances)
assert.Nil(t, err)
@@ -143,3 +144,15 @@ func TestArenaWebsocketApi(t *testing.T) {
readWebsocketType(t, ws, "matchLoad")
readWebsocketType(t, ws, "matchTime")
}
func TestBracketSvgApiDoubleElimination(t *testing.T) {
web := setupTestWeb(t)
web.arena.EventSettings.ElimType = "double"
tournament.CreateTestAlliances(web.arena.Database, 8)
web.arena.CreatePlayoffBracket()
recorder := web.getHttpResponse("/api/bracket/svg")
assert.Equal(t, 200, recorder.Code)
assert.Equal(t, "image/svg+xml", recorder.Header()["Content-Type"][0])
assert.Contains(t, recorder.Body.String(), "Best-of-3")
}

View File

@@ -45,6 +45,7 @@ func TestMatchReviewEditExistingResult(t *testing.T) {
matchResult.MatchType = match.Type
assert.Nil(t, web.arena.Database.CreateMatchResult(matchResult))
tournament.CreateTestAlliances(web.arena.Database, 2)
web.arena.EventSettings.NumElimAlliances = 2
web.arena.CreatePlayoffBracket()
recorder := web.getHttpResponse("/match_review")
@@ -86,6 +87,7 @@ func TestMatchReviewCreateNewResult(t *testing.T) {
Red2: 1002, Red3: 1003, Blue1: 1004, Blue2: 1005, Blue3: 1006, ElimRedAlliance: 1, ElimBlueAlliance: 2}
web.arena.Database.CreateMatch(&match)
tournament.CreateTestAlliances(web.arena.Database, 2)
web.arena.EventSettings.NumElimAlliances = 2
web.arena.CreatePlayoffBracket()
recorder := web.getHttpResponse("/match_review")

View File

@@ -115,6 +115,7 @@ func (web *Web) newHandler() http.Handler {
router.HandleFunc("/alliance_selection/start", web.allianceSelectionStartHandler).Methods("POST")
router.HandleFunc("/api/alliances", web.alliancesApiHandler).Methods("GET")
router.HandleFunc("/api/arena/websocket", web.arenaWebsocketApiHandler).Methods("GET")
router.HandleFunc("/api/bracket/svg", web.bracketSvgApiHandler).Methods("GET")
router.HandleFunc("/api/matches/{type}", web.matchesApiHandler).Methods("GET")
router.HandleFunc("/api/rankings", web.rankingsApiHandler).Methods("GET")
router.HandleFunc("/api/scores", web.getScoresHandler).Methods("GET")