diff --git a/arena.go b/arena.go index ff19802..ab99c5c 100644 --- a/arena.go +++ b/arena.go @@ -50,6 +50,7 @@ type RealtimeScore struct { AutoPreloadedBalls int AutoLeftoverBalls int Fouls []Foul + Cards map[string]string AutoCommitted bool TeleopCommitted bool FoulsCommitted bool @@ -91,6 +92,12 @@ type Arena struct { var mainArena Arena // Named thusly to avoid polluting the global namespace with something more generic. +func NewRealtimeScore() *RealtimeScore { + realtimeScore := new(RealtimeScore) + realtimeScore.Cards = make(map[string]string) + return realtimeScore +} + // Sets the arena to its initial state. func (arena *Arena) Setup() { arena.matchTiming.AutoDurationSec = 10 @@ -213,8 +220,8 @@ func (arena *Arena) LoadMatch(match *Match) error { arena.SetupNetwork() // Reset the realtime scores. - arena.redRealtimeScore = new(RealtimeScore) - arena.blueRealtimeScore = new(RealtimeScore) + arena.redRealtimeScore = NewRealtimeScore() + arena.blueRealtimeScore = NewRealtimeScore() arena.matchLoadTeamsNotifier.Notify(nil) arena.realtimeScoreNotifier.Notify(nil) diff --git a/db/migrations/20140520222523_CreateTeams.sql b/db/migrations/20140520222523_CreateTeams.sql index 141b823..d3bc495 100644 --- a/db/migrations/20140520222523_CreateTeams.sql +++ b/db/migrations/20140520222523_CreateTeams.sql @@ -9,7 +9,8 @@ CREATE TABLE teams ( rookieyear int, robotname VARCHAR(255), accomplishments VARCHAR(1000), - wpakey VARCHAR(16) + wpakey VARCHAR(16), + yellowcard bool ); -- +goose Down diff --git a/db/migrations/20140526180544_CreateMatchResults.sql b/db/migrations/20140526180544_CreateMatchResults.sql index a484243..2e318b7 100644 --- a/db/migrations/20140526180544_CreateMatchResults.sql +++ b/db/migrations/20140526180544_CreateMatchResults.sql @@ -7,7 +7,8 @@ CREATE TABLE match_results ( bluescorejson text, redfoulsjson text, bluefoulsjson text, - cardsjson text + redcardsjson text, + bluecardsjson text ); CREATE UNIQUE INDEX matchid_playnumber ON match_results(matchid, playnumber); diff --git a/displays.go b/displays.go index 868ad83..99c6394 100644 --- a/displays.go +++ b/displays.go @@ -644,22 +644,49 @@ func RefereeDisplayHandler(w http.ResponseWriter, r *http.Request) { match := mainArena.currentMatch matchType := match.CapitalizedType() + red1 := mainArena.AllianceStations["R1"].team + if red1 == nil { + red1 = &Team{} + } + red2 := mainArena.AllianceStations["R2"].team + if red2 == nil { + red2 = &Team{} + } + red3 := mainArena.AllianceStations["R3"].team + if red3 == nil { + red3 = &Team{} + } + blue1 := mainArena.AllianceStations["B1"].team + if blue1 == nil { + blue1 = &Team{} + } + blue2 := mainArena.AllianceStations["B2"].team + if blue2 == nil { + blue2 = &Team{} + } + blue3 := mainArena.AllianceStations["B3"].team + if blue3 == nil { + blue3 = &Team{} + } data := struct { *EventSettings MatchType string MatchDisplayName string - Red1 int - Red2 int - Red3 int - Blue1 int - Blue2 int - Blue3 int + Red1 *Team + Red2 *Team + Red3 *Team + Blue1 *Team + Blue2 *Team + Blue3 *Team RedFouls []Foul BlueFouls []Foul + RedCards map[string]string + BlueCards map[string]string Rules []string EntryEnabled bool - }{eventSettings, matchType, match.DisplayName, match.Red1, match.Red2, match.Red3, match.Blue1, match.Blue2, - match.Blue3, mainArena.redRealtimeScore.Fouls, mainArena.blueRealtimeScore.Fouls, rules, + }{eventSettings, matchType, match.DisplayName, red1, red2, red3, blue1, blue2, blue3, + mainArena.redRealtimeScore.Fouls, mainArena.blueRealtimeScore.Fouls, mainArena.redRealtimeScore.Cards, + mainArena.blueRealtimeScore.Cards, rules, !(mainArena.redRealtimeScore.FoulsCommitted && mainArena.blueRealtimeScore.FoulsCommitted)} err = template.ExecuteTemplate(w, "referee_display.html", data) if err != nil { @@ -766,6 +793,27 @@ func RefereeDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) { } } mainArena.realtimeScoreNotifier.Notify(nil) + case "card": + args := struct { + Alliance string + TeamId int + Card string + }{} + err = mapstructure.Decode(data, &args) + if err != nil { + websocket.WriteError(err.Error()) + continue + } + + // Set the card in the correct alliance's score. + var cards map[string]string + if args.Alliance == "red" { + cards = mainArena.redRealtimeScore.Cards + } else { + cards = mainArena.blueRealtimeScore.Cards + } + cards[strconv.Itoa(args.TeamId)] = args.Card + continue case "commitMatch": mainArena.redRealtimeScore.FoulsCommitted = true mainArena.blueRealtimeScore.FoulsCommitted = true diff --git a/match_play.go b/match_play.go index 870b6e8..3cee80b 100644 --- a/match_play.go +++ b/match_play.go @@ -379,8 +379,8 @@ func MatchPlayWebsocketHandler(w http.ResponseWriter, r *http.Request) { // Saves the given match and result to the database, supplanting any previous result for the match. func CommitMatchScore(match *Match, matchResult *MatchResult) error { if match.Type == "elimination" { - // Adjust the score if necessary for an elimination tie. - matchResult.CorrectEliminationTie() + // Adjust the score if necessary for an elimination DQ or tie. + matchResult.CorrectEliminationScore() } // Store the result in the buffer to be shown in the audience display. @@ -434,6 +434,10 @@ func CommitMatchScore(match *Match, matchResult *MatchResult) error { return err } + if match.Type != "practice" { + db.CalculateTeamCards(match.Type) + } + if match.Type == "qualification" { // Recalculate all the rankings. err = db.CalculateRankings() @@ -471,10 +475,10 @@ func CommitMatchScore(match *Match, matchResult *MatchResult) error { // Saves the realtime result as the final score for the match currently loaded into the arena. func CommitCurrentMatchScore() error { - // TODO(patrick): Set the red/yellow cards. matchResult := MatchResult{MatchId: mainArena.currentMatch.Id, RedScore: mainArena.redRealtimeScore.CurrentScore, BlueScore: mainArena.blueRealtimeScore.CurrentScore, - RedFouls: mainArena.redRealtimeScore.Fouls, BlueFouls: mainArena.blueRealtimeScore.Fouls} + RedFouls: mainArena.redRealtimeScore.Fouls, BlueFouls: mainArena.blueRealtimeScore.Fouls, + RedCards: mainArena.redRealtimeScore.Cards, BlueCards: mainArena.blueRealtimeScore.Cards} return CommitMatchScore(mainArena.currentMatch, &matchResult) } diff --git a/match_result.go b/match_result.go index 45d4a72..602b84b 100644 --- a/match_result.go +++ b/match_result.go @@ -17,7 +17,8 @@ type MatchResult struct { BlueScore Score RedFouls []Foul BlueFouls []Foul - Cards Cards + RedCards map[string]string + BlueCards map[string]string } type MatchResultDb struct { @@ -28,7 +29,8 @@ type MatchResultDb struct { BlueScoreJson string RedFoulsJson string BlueFoulsJson string - CardsJson string + RedCardsJson string + BlueCardsJson string } type Score struct { @@ -42,6 +44,7 @@ type Score struct { AutoClearDead int Cycles []Cycle ElimTiebreaker int + ElimDq bool } type Cycle struct { @@ -60,11 +63,6 @@ type Foul struct { IsTechnical bool } -type Cards struct { - YellowCardTeamIds []int - RedCardTeamIds []int -} - type ScoreSummary struct { AutoPoints int AssistPoints int @@ -82,8 +80,8 @@ func NewMatchResult() *MatchResult { matchResult.BlueScore.Cycles = []Cycle{} matchResult.RedFouls = []Foul{} matchResult.BlueFouls = []Foul{} - matchResult.Cards.YellowCardTeamIds = []int{} - matchResult.Cards.RedCardTeamIds = []int{} + matchResult.RedCards = make(map[string]string) + matchResult.BlueCards = make(map[string]string) return matchResult } @@ -149,7 +147,20 @@ func (matchResult *MatchResult) BlueScoreSummary() *ScoreSummary { return scoreSummary(&matchResult.BlueScore, matchResult.RedFouls) } -func (matchResult *MatchResult) CorrectEliminationTie() { +// Checks the score for disqualifications or a tie and adjusts it appropriately. +func (matchResult *MatchResult) CorrectEliminationScore() { + matchResult.RedScore.ElimDq = false + for _, card := range matchResult.RedCards { + if card == "red" { + matchResult.RedScore.ElimDq = true + } + } + for _, card := range matchResult.BlueCards { + if card == "red" { + matchResult.BlueScore.ElimDq = true + } + } + matchResult.RedScore.ElimTiebreaker = 0 matchResult.BlueScore.ElimTiebreaker = 0 redScore := matchResult.RedScoreSummary() @@ -193,6 +204,11 @@ func (matchResult *MatchResult) CorrectEliminationTie() { func scoreSummary(score *Score, opponentFouls []Foul) *ScoreSummary { summary := new(ScoreSummary) + // Leave the score at zero if the team was disqualified. + if score.ElimDq { + return summary + } + // Calculate autonomous score. summary.AutoPoints = 5*score.AutoMobilityBonuses + 20*score.AutoHighHot + 15*score.AutoHigh + 11*score.AutoLowHot + 6*score.AutoLow @@ -252,7 +268,10 @@ func (matchResult *MatchResult) serialize() (*MatchResultDb, error) { if err := serializeHelper(&matchResultDb.BlueFoulsJson, matchResult.BlueFouls); err != nil { return nil, err } - if err := serializeHelper(&matchResultDb.CardsJson, matchResult.Cards); err != nil { + if err := serializeHelper(&matchResultDb.RedCardsJson, matchResult.RedCards); err != nil { + return nil, err + } + if err := serializeHelper(&matchResultDb.BlueCardsJson, matchResult.BlueCards); err != nil { return nil, err } return &matchResultDb, nil @@ -282,7 +301,10 @@ func (matchResultDb *MatchResultDb) deserialize() (*MatchResult, error) { if err := json.Unmarshal([]byte(matchResultDb.BlueFoulsJson), &matchResult.BlueFouls); err != nil { return nil, err } - if err := json.Unmarshal([]byte(matchResultDb.CardsJson), &matchResult.Cards); err != nil { + if err := json.Unmarshal([]byte(matchResultDb.RedCardsJson), &matchResult.RedCards); err != nil { + return nil, err + } + if err := json.Unmarshal([]byte(matchResultDb.BlueCardsJson), &matchResult.BlueCards); err != nil { return nil, err } return &matchResult, nil diff --git a/match_result_test.go b/match_result_test.go index 802a9ac..6faf617 100644 --- a/match_result_test.go +++ b/match_result_test.go @@ -147,21 +147,22 @@ func TestScoreSummary(t *testing.T) { assert.Equal(t, 593, blueSummary.Score) } -func TestCorrectEliminationTie(t *testing.T) { +func TestCorrectEliminationScore(t *testing.T) { + // TODO(patrick): Test proper calculation of DQ. matchResult := MatchResult{} - matchResult.CorrectEliminationTie() + matchResult.CorrectEliminationScore() assert.Equal(t, 0, matchResult.RedScore.ElimTiebreaker) assert.Equal(t, 0, matchResult.BlueScore.ElimTiebreaker) matchResult.RedScore.AutoHighHot = 1 matchResult.RedFouls = []Foul{Foul{}} - matchResult.CorrectEliminationTie() + matchResult.CorrectEliminationScore() assert.Equal(t, 0, matchResult.RedScore.ElimTiebreaker) assert.Equal(t, 1, matchResult.BlueScore.ElimTiebreaker) assert.Equal(t, 1, matchResult.BlueScoreSummary().Score-matchResult.RedScoreSummary().Score) matchResult.RedScore, matchResult.BlueScore = matchResult.BlueScore, matchResult.RedScore matchResult.RedFouls, matchResult.BlueFouls = matchResult.BlueFouls, matchResult.RedFouls - matchResult.CorrectEliminationTie() + matchResult.CorrectEliminationScore() assert.Equal(t, 1, matchResult.RedScore.ElimTiebreaker) assert.Equal(t, 0, matchResult.BlueScore.ElimTiebreaker) assert.Equal(t, 1, matchResult.RedScoreSummary().Score-matchResult.BlueScoreSummary().Score) @@ -169,36 +170,36 @@ func TestCorrectEliminationTie(t *testing.T) { matchResult = MatchResult{} matchResult.RedScore.Cycles = []Cycle{Cycle{Assists: 2, ScoredHigh: true}} matchResult.BlueScore.Cycles = []Cycle{Cycle{Truss: true, Catch: true}} - matchResult.CorrectEliminationTie() + matchResult.CorrectEliminationScore() assert.Equal(t, 1, matchResult.RedScore.ElimTiebreaker) assert.Equal(t, 0, matchResult.BlueScore.ElimTiebreaker) matchResult.RedScore, matchResult.BlueScore = matchResult.BlueScore, matchResult.RedScore matchResult.RedFouls, matchResult.BlueFouls = matchResult.BlueFouls, matchResult.RedFouls - matchResult.CorrectEliminationTie() + matchResult.CorrectEliminationScore() assert.Equal(t, 0, matchResult.RedScore.ElimTiebreaker) assert.Equal(t, 1, matchResult.BlueScore.ElimTiebreaker) matchResult = MatchResult{} matchResult.RedScore.Cycles = []Cycle{Cycle{Truss: true, Catch: true}} matchResult.BlueScore.AutoHighHot = 1 - matchResult.CorrectEliminationTie() + matchResult.CorrectEliminationScore() assert.Equal(t, 0, matchResult.RedScore.ElimTiebreaker) assert.Equal(t, 1, matchResult.BlueScore.ElimTiebreaker) matchResult.RedScore, matchResult.BlueScore = matchResult.BlueScore, matchResult.RedScore matchResult.RedFouls, matchResult.BlueFouls = matchResult.BlueFouls, matchResult.RedFouls - matchResult.CorrectEliminationTie() + matchResult.CorrectEliminationScore() assert.Equal(t, 1, matchResult.RedScore.ElimTiebreaker) assert.Equal(t, 0, matchResult.BlueScore.ElimTiebreaker) matchResult = MatchResult{} matchResult.RedScore.Cycles = []Cycle{Cycle{Truss: true, Catch: true}} matchResult.BlueScore.Cycles = []Cycle{Cycle{ScoredHigh: true}, Cycle{ScoredHigh: true}} - matchResult.CorrectEliminationTie() + matchResult.CorrectEliminationScore() assert.Equal(t, 1, matchResult.RedScore.ElimTiebreaker) assert.Equal(t, 0, matchResult.BlueScore.ElimTiebreaker) matchResult.RedScore, matchResult.BlueScore = matchResult.BlueScore, matchResult.RedScore matchResult.RedFouls, matchResult.BlueFouls = matchResult.BlueFouls, matchResult.RedFouls - matchResult.CorrectEliminationTie() + matchResult.CorrectEliminationScore() assert.Equal(t, 0, matchResult.RedScore.ElimTiebreaker) assert.Equal(t, 1, matchResult.BlueScore.ElimTiebreaker) } @@ -209,10 +210,11 @@ func buildTestMatchResult(matchId int, playNumber int) MatchResult { cycle3 := Cycle{1, true, false, false, false, true} fouls := []Foul{Foul{25, "G22", 25.2, false}, Foul{25, "G18", 150, false}, Foul{1868, "G20", 0, true}} matchResult := MatchResult{MatchId: matchId, PlayNumber: playNumber} - matchResult.RedScore = Score{1, 2, 3, 4, 5, 6, 7, 8, []Cycle{cycle1, cycle2, cycle3}, 0} - matchResult.BlueScore = Score{7, 6, 5, 4, 3, 2, 1, 0, []Cycle{cycle3, cycle1, cycle1, cycle1}, 0} + matchResult.RedScore = Score{1, 2, 3, 4, 5, 6, 7, 8, []Cycle{cycle1, cycle2, cycle3}, 0, false} + matchResult.BlueScore = Score{7, 6, 5, 4, 3, 2, 1, 0, []Cycle{cycle3, cycle1, cycle1, cycle1}, 0, false} matchResult.RedFouls = fouls matchResult.BlueFouls = []Foul{} - matchResult.Cards = Cards{[]int{1868}, []int{}} + matchResult.RedCards = map[string]string{"1868": "yellow"} + matchResult.BlueCards = map[string]string{} return matchResult } diff --git a/match_review.go b/match_review.go index edc8af6..662211e 100644 --- a/match_review.go +++ b/match_review.go @@ -106,7 +106,7 @@ func MatchReviewEditPostHandler(w http.ResponseWriter, r *http.Request) { matchResultJson := MatchResultDb{Id: matchResult.Id, MatchId: match.Id, PlayNumber: matchResult.PlayNumber, RedScoreJson: r.PostFormValue("redScoreJson"), BlueScoreJson: r.PostFormValue("blueScoreJson"), RedFoulsJson: r.PostFormValue("redFoulsJson"), BlueFoulsJson: r.PostFormValue("blueFoulsJson"), - CardsJson: r.PostFormValue("cardsJson")} + RedCardsJson: r.PostFormValue("redCardsJson"), BlueCardsJson: r.PostFormValue("blueCardsJson")} // Deserialize the JSON using the same mechanism as to store scoring information in the database. matchResult, err = matchResultJson.deserialize() diff --git a/ranking.go b/ranking.go index 3d91d8d..af5cb1c 100644 --- a/ranking.go +++ b/ranking.go @@ -8,6 +8,7 @@ package main import ( "math/rand" "sort" + "strconv" ) type Ranking struct { @@ -122,6 +123,57 @@ func (database *Database) CalculateRankings() error { return nil } +// Checks all the match results for yellow and red cards, and updates the team model accordingly. +func (database *Database) CalculateTeamCards(matchType string) error { + teams, err := database.GetAllTeams() + if err != nil { + return err + } + teamsMap := make(map[string]Team) + for _, team := range teams { + team.YellowCard = false + teamsMap[strconv.Itoa(team.Id)] = team + } + + matches, err := database.GetMatchesByType(matchType) + if err != nil { + return err + } + for _, match := range matches { + if match.Status != "complete" { + continue + } + matchResult, err := database.GetMatchResultForMatch(match.Id) + if err != nil { + return err + } + + // Mark the team as having a yellow card if they got either a yellow or red in a previous match. + for teamId, card := range matchResult.RedCards { + if team, ok := teamsMap[teamId]; ok && card != "" { + team.YellowCard = true + teamsMap[teamId] = team + } + } + for teamId, card := range matchResult.BlueCards { + if team, ok := teamsMap[teamId]; ok && card != "" { + team.YellowCard = true + teamsMap[teamId] = team + } + } + } + + // Save the teams to the database. + for _, team := range teamsMap { + err = db.SaveTeam(&team) + if err != nil { + return err + } + } + + return nil +} + func addMatchResultToRankings(rankings map[int]*Ranking, teamId int, matchResult *MatchResult, isRed bool) { ranking := rankings[teamId] if ranking == nil { @@ -131,11 +183,15 @@ func addMatchResultToRankings(rankings map[int]*Ranking, teamId int, matchResult ranking.Played += 1 // Don't award any points if the team was disqualified. - for _, dqTeamId := range matchResult.Cards.RedCardTeamIds { - if teamId == dqTeamId { - ranking.Disqualifications += 1 - return - } + var cards map[string]string + if isRed { + cards = matchResult.RedCards + } else { + cards = matchResult.BlueCards + } + if card, ok := cards[strconv.Itoa(teamId)]; ok && card == "red" { + ranking.Disqualifications += 1 + return } var ownScore, opponentScore *ScoreSummary diff --git a/ranking_test.go b/ranking_test.go index 9742281..82dd81b 100644 --- a/ranking_test.go +++ b/ranking_test.go @@ -190,7 +190,7 @@ func setupMatchResultsForRankings(db *Database) { Blue3: 6, Status: "complete"} db.CreateMatch(&match1) matchResult1 := buildTestMatchResult(match1.Id, 1) - matchResult1.Cards = Cards{[]int{}, []int{2}} + matchResult1.RedCards = map[string]string{"2": "red"} db.CreateMatchResult(&matchResult1) match2 := Match{Type: "qualification", DisplayName: "2", Red1: 1, Red2: 3, Red3: 5, Blue1: 2, Blue2: 4, @@ -212,20 +212,17 @@ func setupMatchResultsForRankings(db *Database) { Blue3: 6, Status: "complete"} db.CreateMatch(&match4) matchResult4 := buildTestMatchResult(match4.Id, 1) - matchResult4.Cards = Cards{[]int{}, []int{}} db.CreateMatchResult(&matchResult4) match5 := Match{Type: "elimination", DisplayName: "F-1", Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, Blue3: 6, Status: "complete"} db.CreateMatch(&match5) matchResult5 := buildTestMatchResult(match5.Id, 1) - matchResult5.Cards = Cards{[]int{}, []int{}} db.CreateMatchResult(&matchResult5) match6 := Match{Type: "qualification", DisplayName: "4", Red1: 7, Red2: 8, Red3: 9, Blue1: 10, Blue2: 11, Blue3: 12, Status: ""} db.CreateMatch(&match6) matchResult6 := buildTestMatchResult(match6.Id, 1) - matchResult6.Cards = Cards{[]int{}, []int{}} db.CreateMatchResult(&matchResult6) } diff --git a/setup_alliance_selection.go b/setup_alliance_selection.go index 9e767ee..76e41a9 100644 --- a/setup_alliance_selection.go +++ b/setup_alliance_selection.go @@ -173,6 +173,13 @@ func AllianceSelectionFinalizeHandler(w http.ResponseWriter, r *http.Request) { return } + // Reset yellow cards. + err = db.CalculateTeamCards("elimination") + if err != nil { + handleWebErr(w, err) + return + } + if eventSettings.TbaPublishingEnabled { // Publish alliances and schedule to The Blue Alliance. err = PublishAlliances() diff --git a/static/css/referee_display.css b/static/css/referee_display.css index 83d82a3..056b04d 100644 --- a/static/css/referee_display.css +++ b/static/css/referee_display.css @@ -28,7 +28,6 @@ h3, h4 { .btn-referee[data-selected="true"] { border: 5px solid #fc0; margin: 0px 5px; - background-color: #fc0; } tr { font-size: 15px; @@ -39,3 +38,21 @@ tr { .row-blue { background-color: #028fc0; } +.btn-card { + width: 120px; + margin: 5px 5px; + font-size: 30px; + cursor: default; + background-color: #444; + color: #fff; +} +.btn-card[data-card="yellow"] { + background-color: #dd0; +} +.btn-card[data-card="red"] { + background-color: #cb210e; +} +.btn-card[data-old-yellow-card="true"] { + border: 5px solid #ff0; + margin: 0px 5px; +} diff --git a/static/js/match_review.js b/static/js/match_review.js index 6ae60b4..be393e3 100644 --- a/static/js/match_review.js +++ b/static/js/match_review.js @@ -15,20 +15,16 @@ $("form").submit(function() { var blueScoreJson = JSON.stringify(allianceResults["blue"].score); var redFoulsJson = JSON.stringify(allianceResults["red"].fouls); var blueFoulsJson = JSON.stringify(allianceResults["blue"].fouls); - - // Merge the red and blue cards data since that's what the database model expects. - var mergedCards = {YellowCardTeamIds: allianceResults["red"].cards.YellowCardTeamIds. - concat(allianceResults["blue"].cards.YellowCardTeamIds), - RedCardTeamIds:allianceResults["red"].cards.RedCardTeamIds. - concat(allianceResults["blue"].cards.RedCardTeamIds)}; - var cardsJson = JSON.stringify(mergedCards); + var redCardsJson = JSON.stringify(allianceResults["red"].cards); + var blueCardsJson = JSON.stringify(allianceResults["blue"].cards); // Inject the JSON data into the form as hidden inputs. $("").attr("type", "hidden").attr("name", "redScoreJson").attr("value", redScoreJson).appendTo("form"); $("").attr("type", "hidden").attr("name", "blueScoreJson").attr("value", blueScoreJson).appendTo("form"); $("").attr("type", "hidden").attr("name", "redFoulsJson").attr("value", redFoulsJson).appendTo("form"); $("").attr("type", "hidden").attr("name", "blueFoulsJson").attr("value", blueFoulsJson).appendTo("form"); - $("").attr("type", "hidden").attr("name", "cardsJson").attr("value", cardsJson).appendTo("form"); + $("").attr("type", "hidden").attr("name", "redCardsJson").attr("value", redCardsJson).appendTo("form"); + $("").attr("type", "hidden").attr("name", "blueCardsJson").attr("value", blueCardsJson).appendTo("form"); return true; }); @@ -48,32 +44,34 @@ var renderResults = function(alliance) { $("input[name=" + alliance + "AutoClearHigh]").val(result.score.AutoClearHigh); $("input[name=" + alliance + "AutoClearLow]").val(result.score.AutoClearLow); - $.each(result.score.Cycles, function(k, v) { - $("#" + alliance + "Cycle" + k + "Title").text("Cycle " + (k + 1)); - $("input[name=" + alliance + "Cycle" + k + "Assists][value=" + v.Assists + "]").prop("checked", true); + if (result.score.Cycles != null) { + $.each(result.score.Cycles, function(k, v) { + $("#" + alliance + "Cycle" + k + "Title").text("Cycle " + (k + 1)); + $("input[name=" + alliance + "Cycle" + k + "Assists][value=" + v.Assists + "]").prop("checked", true); - var trussCatch; - if (v.Truss && v.Catch) { - trussCatch = "TC"; - } else if (v.Truss) { - trussCatch = "T"; - } else { - trussCatch = "N"; - } - $("input[name=" + alliance + "Cycle" + k + "TrussCatch][value=" + trussCatch + "]").prop("checked", true); + var trussCatch; + if (v.Truss && v.Catch) { + trussCatch = "TC"; + } else if (v.Truss) { + trussCatch = "T"; + } else { + trussCatch = "N"; + } + $("input[name=" + alliance + "Cycle" + k + "TrussCatch][value=" + trussCatch + "]").prop("checked", true); - var cycleEnd; - if (v.ScoredHigh) { - cycleEnd = "SH"; - } else if (v.ScoredLow) { - cycleEnd = "SL"; - } else if (v.DeadBall) { - cycleEnd = "DB"; - } else { - cycleEnd = "DE"; - } - $("input[name=" + alliance + "Cycle" + k + "End][value=" + cycleEnd + "]").prop("checked", true); - }); + var cycleEnd; + if (v.ScoredHigh) { + cycleEnd = "SH"; + } else if (v.ScoredLow) { + cycleEnd = "SL"; + } else if (v.DeadBall) { + cycleEnd = "DB"; + } else { + cycleEnd = "DE"; + } + $("input[name=" + alliance + "Cycle" + k + "End][value=" + cycleEnd + "]").prop("checked", true); + }); + } if (result.fouls != null) { $.each(result.fouls, function(k, v) { @@ -84,14 +82,9 @@ var renderResults = function(alliance) { }); } - if (result.cards.YellowCardTeamIds != null) { - $.each(result.cards.YellowCardTeamIds, function(k, v) { - $("input[name=" + alliance + "Team" + v + "Card][value=Y]").prop("checked", true); - }); - } - if (result.cards.RedCardTeamIds != null) { - $.each(result.cards.RedCardTeamIds, function(k, v) { - $("input[name=" + alliance + "Team" + v + "Card][value=R]").prop("checked", true); + if (result.cards != null) { + $.each(result.cards, function(k, v) { + $("input[name=" + alliance + "Team" + k + "Card][value=" + v + "]").prop("checked", true); }); } } @@ -145,16 +138,9 @@ var updateResults = function(alliance) { result.fouls.push(foul); } - result.cards.YellowCardTeamIds = [] - result.cards.RedCardTeamIds = [] + result.cards = {}; $.each([result.team1, result.team2, result.team3], function(i, team) { - switch (formData[alliance + "Team" + team + "Card"]) { - case "Y": - result.cards.YellowCardTeamIds.push(team); - break - case "R": - result.cards.RedCardTeamIds.push(team); - } + result.cards[team] = formData[alliance + "Team" + team + "Card"]; }); } diff --git a/static/js/referee_display.js b/static/js/referee_display.js index 8fef939..19d332b 100644 --- a/static/js/referee_display.js +++ b/static/js/referee_display.js @@ -64,6 +64,19 @@ var deleteFoul = function(alliance, team, rule, timeSec, isTech) { TimeInMatchSec: timeSec, IsTechnical: isTech}); }; +// Cycles through no card, yellow card, and red card. +var cycleCard = function(cardButton) { + var newCard = ""; + if ($(cardButton).attr("data-card") == "") { + newCard = "yellow"; + } else if ($(cardButton).attr("data-card") == "yellow") { + newCard = "red"; + } + websocket.send("card", {Alliance: $(cardButton).attr("data-alliance"), + TeamId: parseInt($(cardButton).attr("data-card-team")), Card: newCard}); + $(cardButton).attr("data-card", newCard); +}; + // Signals the scorekeeper that foul entry is complete for this match. var commitMatch = function() { websocket.send("commitMatch"); diff --git a/team.go b/team.go index ec2abdd..a94e49c 100644 --- a/team.go +++ b/team.go @@ -16,6 +16,7 @@ type Team struct { RobotName string Accomplishments string WpaKey string + YellowCard bool } func (database *Database) CreateTeam(team *Team) error { diff --git a/templates/edit_match_result.html b/templates/edit_match_result.html index ad1d060..06d57f6 100644 --- a/templates/edit_match_result.html +++ b/templates/edit_match_result.html @@ -208,19 +208,19 @@