Update match review interface for 2016.

This commit is contained in:
Patrick Fairbank
2016-08-07 20:18:53 -07:00
parent 8af83ea7e7
commit e01dc31e6d
4 changed files with 209 additions and 149 deletions

View File

@@ -116,8 +116,9 @@ func MatchReviewEditPostHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
matchResultJson := MatchResultDb{Id: matchResult.Id, MatchId: match.Id, PlayNumber: matchResult.PlayNumber,
RedScoreJson: r.PostFormValue("redScoreJson"), BlueScoreJson: r.PostFormValue("blueScoreJson"),
RedCardsJson: r.PostFormValue("redCardsJson"), BlueCardsJson: r.PostFormValue("blueCardsJson")}
MatchType: matchResult.MatchType, RedScoreJson: r.PostFormValue("redScoreJson"),
BlueScoreJson: r.PostFormValue("blueScoreJson"), 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()

View File

@@ -4,6 +4,7 @@
package main
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
)
@@ -39,91 +40,86 @@ func TestMatchReview(t *testing.T) {
}
func TestMatchReviewEditExistingResult(t *testing.T) {
// TODO(patrick): Update test for 2016.
/*
clearDb()
defer clearDb()
var err error
db, err = OpenDatabase(testDbPath)
assert.Nil(t, err)
defer db.Close()
eventSettings, _ = db.GetEventSettings()
mainArena.Setup()
clearDb()
defer clearDb()
var err error
db, err = OpenDatabase(testDbPath)
assert.Nil(t, err)
defer db.Close()
eventSettings, _ = db.GetEventSettings()
mainArena.Setup()
match := Match{Type: "elimination", DisplayName: "QF4-3", Status: "complete", Winner: "R", Red1: 1001,
Red2: 1002, Red3: 1003, Blue1: 1004, Blue2: 1005, Blue3: 1006}
db.CreateMatch(&match)
matchResult := buildTestMatchResult(match.Id, 1)
db.CreateMatchResult(&matchResult)
createTestAlliances(db, 2)
match := Match{Type: "elimination", DisplayName: "QF4-3", Status: "complete", Winner: "R", Red1: 1001,
Red2: 1002, Red3: 1003, Blue1: 1004, Blue2: 1005, Blue3: 1006}
db.CreateMatch(&match)
matchResult := buildTestMatchResult(match.Id, 1)
matchResult.MatchType = match.Type
db.CreateMatchResult(&matchResult)
createTestAlliances(db, 2)
recorder := getHttpResponse("/match_review")
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "QF4-3")
assert.Contains(t, recorder.Body.String(), "92") // The red score
assert.Contains(t, recorder.Body.String(), "104") // The blue score
recorder := getHttpResponse("/match_review")
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "QF4-3")
assert.Contains(t, recorder.Body.String(), "176") // The red score
assert.Contains(t, recorder.Body.String(), "113") // The blue score
// Check response for non-existent match.
recorder = getHttpResponse(fmt.Sprintf("/match_review/%d/edit", 12345))
assert.Equal(t, 500, recorder.Code)
assert.Contains(t, recorder.Body.String(), "No such match")
// Check response for non-existent match.
recorder = getHttpResponse(fmt.Sprintf("/match_review/%d/edit", 12345))
assert.Equal(t, 500, recorder.Code)
assert.Contains(t, recorder.Body.String(), "No such match")
recorder = getHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id))
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "QF4-3")
recorder = getHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id))
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "QF4-3")
// Update the score to something else.
postBody := "redScoreJson={\"AutoRobotSet\":true}&blueScoreJson={\"Stacks\":[{\"Totes\":6,\"Container\":true,\"Litter\":true}]," +
"\"Fouls\":[{\"TeamId\":973,\"Rule\":\"G22\"}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}"
recorder = postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody)
assert.Equal(t, 302, recorder.Code)
// Update the score to something else.
postBody := "redScoreJson={\"AutoLowGoals\":5}&blueScoreJson={\"DefensesCrossed\":[2,2,1,2,2]," +
"\"Fouls\":[{\"TeamId\":973,\"Rule\":\"G22\"}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}"
recorder = postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody)
assert.Equal(t, 302, recorder.Code)
// Check for the updated scores back on the match list page.
recorder = getHttpResponse("/match_review")
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "QF4-3")
assert.Contains(t, recorder.Body.String(), "4") // The red score
assert.Contains(t, recorder.Body.String(), "36") // The blue score
*/
// Check for the updated scores back on the match list page.
recorder = getHttpResponse("/match_review")
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "QF4-3")
assert.Contains(t, recorder.Body.String(), "30") // The red score
assert.Contains(t, recorder.Body.String(), "65") // The blue score
}
func TestMatchReviewCreateNewResult(t *testing.T) {
// TODO(patrick): Update test for 2016.
/*
clearDb()
defer clearDb()
var err error
db, err = OpenDatabase(testDbPath)
assert.Nil(t, err)
defer db.Close()
eventSettings, _ = db.GetEventSettings()
clearDb()
defer clearDb()
var err error
db, err = OpenDatabase(testDbPath)
assert.Nil(t, err)
defer db.Close()
eventSettings, _ = db.GetEventSettings()
match := Match{Type: "elimination", DisplayName: "QF4-3", Status: "complete", Winner: "R", Red1: 1001,
Red2: 1002, Red3: 1003, Blue1: 1004, Blue2: 1005, Blue3: 1006}
db.CreateMatch(&match)
createTestAlliances(db, 2)
match := Match{Type: "elimination", DisplayName: "QF4-3", Status: "complete", Winner: "R", Red1: 1001,
Red2: 1002, Red3: 1003, Blue1: 1004, Blue2: 1005, Blue3: 1006}
db.CreateMatch(&match)
createTestAlliances(db, 2)
recorder := getHttpResponse("/match_review")
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "QF4-3")
assert.NotContains(t, recorder.Body.String(), "92") // The red score
assert.NotContains(t, recorder.Body.String(), "104") // The blue score
recorder := getHttpResponse("/match_review")
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "QF4-3")
assert.NotContains(t, recorder.Body.String(), "176") // The red score
assert.NotContains(t, recorder.Body.String(), "113") // The blue score
recorder = getHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id))
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "QF4-3")
recorder = getHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id))
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "QF4-3")
// Update the score to something else.
postBody := "redScoreJson={\"AutoToteSet\":true}&blueScoreJson={\"Stacks\":[{\"Totes\":6,\"Container\":true,\"Litter\":true}]," +
"\"Fouls\":[{\"TeamId\":973,\"Rule\":\"G22\"}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}"
recorder = postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody)
assert.Equal(t, 302, recorder.Code)
// Update the score to something else.
postBody := "redScoreJson={\"AutoDefensesReached\":3}&blueScoreJson={\"HighGoals\":3," +
"\"Fouls\":[{\"TeamId\":973,\"Rule\":\"G22\"}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}"
recorder = postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody)
assert.Equal(t, 302, recorder.Code)
// Check for the updated scores back on the match list page.
recorder = getHttpResponse("/match_review")
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "QF4-3")
assert.Contains(t, recorder.Body.String(), "6") // The red score
assert.Contains(t, recorder.Body.String(), "36") // The blue score
*/
// Check for the updated scores back on the match list page.
recorder = getHttpResponse("/match_review")
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "QF4-3")
assert.Contains(t, recorder.Body.String(), "11") // The red score
assert.Contains(t, recorder.Body.String(), "15") // The blue score
}

View File

@@ -32,26 +32,30 @@ var renderResults = function(alliance) {
$("#" + alliance + "Score").html(scoreContent);
// Set the values of the form fields from the JSON results data.
$("input[name=" + alliance + "AutoRobotSet]").prop("checked", result.score.AutoRobotSet);
$("input[name=" + alliance + "AutoContainerSet]").prop("checked", result.score.AutoContainerSet);
$("input[name=" + alliance + "AutoToteSet]").prop("checked", result.score.AutoToteSet);
$("input[name=" + alliance + "AutoStackedToteSet]").prop("checked", result.score.AutoStackedToteSet);
$("input[name=" + alliance + "CoopertitionSet]").prop("checked", result.score.CoopertitionSet);
$("input[name=" + alliance + "CoopertitionStack]").prop("checked", result.score.CoopertitionStack);
$("input[name=" + alliance + "AutoDefense1Crossings]").val(result.score.AutoDefensesCrossed[0]);
$("input[name=" + alliance + "AutoDefense2Crossings]").val(result.score.AutoDefensesCrossed[1]);
$("input[name=" + alliance + "AutoDefense3Crossings]").val(result.score.AutoDefensesCrossed[2]);
$("input[name=" + alliance + "AutoDefense4Crossings]").val(result.score.AutoDefensesCrossed[3]);
$("input[name=" + alliance + "AutoDefense5Crossings]").val(result.score.AutoDefensesCrossed[4]);
$("input[name=" + alliance + "AutoDefensesReached]").val(result.score.AutoDefensesReached);
$("input[name=" + alliance + "AutoHighGoals]").val(result.score.AutoHighGoals);
$("input[name=" + alliance + "AutoLowGoals]").val(result.score.AutoLowGoals);
if (result.score.Stacks != null) {
$.each(result.score.Stacks, function(k, v) {
$("#" + alliance + "Stack" + k + "Title").text("Stack " + (k + 1));
$("input[name=" + alliance + "Stack" + k + "Totes]").val(v.Totes);
$("input[name=" + alliance + "Stack" + k + "Container]").prop("checked", v.Container);
$("input[name=" + alliance + "Stack" + k + "Litter]").prop("checked", v.Litter);
});
}
$("input[name=" + alliance + "Defense1Crossings]").val(result.score.DefensesCrossed[0]);
$("input[name=" + alliance + "Defense2Crossings]").val(result.score.DefensesCrossed[1]);
$("input[name=" + alliance + "Defense3Crossings]").val(result.score.DefensesCrossed[2]);
$("input[name=" + alliance + "Defense4Crossings]").val(result.score.DefensesCrossed[3]);
$("input[name=" + alliance + "Defense5Crossings]").val(result.score.DefensesCrossed[4]);
$("input[name=" + alliance + "HighGoals]").val(result.score.HighGoals);
$("input[name=" + alliance + "LowGoals]").val(result.score.LowGoals);
$("input[name=" + alliance + "Challenges]").val(result.score.Challenges);
$("input[name=" + alliance + "Scales]").val(result.score.Scales);
if (result.score.Fouls != null) {
$.each(result.score.Fouls, function(k, v) {
$("input[name=" + alliance + "Foul" + k + "Team][value=" + v.TeamId + "]").prop("checked", true);
$("input[name=" + alliance + "Foul" + k + "Rule]").val(v.Rule);
$("input[name=" + alliance + "Foul" + k + "IsTechnical]").prop("checked", v.IsTechnical);
$("input[name=" + alliance + "Foul" + k + "Time]").val(v.TimeInMatchSec);
});
}
@@ -71,26 +75,31 @@ var updateResults = function(alliance) {
formData[v.name] = v.value;
});
result.score.AutoRobotSet = formData[alliance + "AutoRobotSet"] == "on";
result.score.AutoContainerSet = formData[alliance + "AutoContainerSet"] == "on";
result.score.AutoToteSet = formData[alliance + "AutoToteSet"] == "on";
result.score.AutoStackedToteSet = formData[alliance + "AutoStackedToteSet"] == "on";
result.score.CoopertitionSet = formData[alliance + "CoopertitionSet"] == "on";
result.score.CoopertitionStack = formData[alliance + "CoopertitionStack"] == "on";
result.score.Stacks = [];
for (var i = 0; formData[alliance + "Stack" + i + "Totes"]; i++) {
var prefix = alliance + "Stack" + i;
var stack = {Totes: parseInt(formData[prefix + "Totes"]),
Container: formData[prefix + "Container"] == "on",
Litter: formData[prefix + "Litter"] == "on"}
result.score.Stacks.push(stack);
}
result.score.AutoDefensesCrossed = [];
result.score.AutoDefensesCrossed.push(parseInt(formData[alliance + "AutoDefense1Crossings"]));
result.score.AutoDefensesCrossed.push(parseInt(formData[alliance + "AutoDefense2Crossings"]));
result.score.AutoDefensesCrossed.push(parseInt(formData[alliance + "AutoDefense3Crossings"]));
result.score.AutoDefensesCrossed.push(parseInt(formData[alliance + "AutoDefense4Crossings"]));
result.score.AutoDefensesCrossed.push(parseInt(formData[alliance + "AutoDefense5Crossings"]));
result.score.AutoDefensesReached = parseInt(formData[alliance + "AutoDefensesReached"]);
result.score.AutoHighGoals = parseInt(formData[alliance + "AutoHighGoals"]);
result.score.AutoLowGoals = parseInt(formData[alliance + "AutoLowGoals"]);
result.score.DefensesCrossed = [];
result.score.DefensesCrossed.push(parseInt(formData[alliance + "Defense1Crossings"]));
result.score.DefensesCrossed.push(parseInt(formData[alliance + "Defense2Crossings"]));
result.score.DefensesCrossed.push(parseInt(formData[alliance + "Defense3Crossings"]));
result.score.DefensesCrossed.push(parseInt(formData[alliance + "Defense4Crossings"]));
result.score.DefensesCrossed.push(parseInt(formData[alliance + "Defense5Crossings"]));
result.score.HighGoals = parseInt(formData[alliance + "HighGoals"]);
result.score.LowGoals = parseInt(formData[alliance + "LowGoals"]);
result.score.Challenges = parseInt(formData[alliance + "Challenges"]);
result.score.Scales = parseInt(formData[alliance + "Scales"]);
result.score.Fouls = [];
for (var i = 0; formData[alliance + "Foul" + i + "Time"]; i++) {
var prefix = alliance + "Foul" + i;
var foul = {TeamId: parseInt(formData[prefix + "Team"]), Rule: formData[prefix + "Rule"],
IsTechnical: formData[prefix + "IsTechnical"] == "on",
TimeInMatchSec: parseFloat(formData[prefix + "Time"])};
result.score.Fouls.push(foul);
}
@@ -101,22 +110,6 @@ var updateResults = function(alliance) {
});
}
// Appends a blank stack to the end of the list.
var addStack = function(alliance) {
updateResults(alliance);
var result = allianceResults[alliance];
result.score.Stacks.push({Totes: 0, Container: false, Litter: false})
renderResults(alliance);
}
// Removes the given stack from the list.
var deleteStack = function(alliance, index) {
updateResults(alliance);
var result = allianceResults[alliance];
result.score.Stacks.splice(index, 1);
renderResults(alliance);
}
// Appends a blank foul to the end of the list.
var addFoul = function(alliance) {
updateResults(alliance);

View File

@@ -27,40 +27,104 @@
<div class="well well-{{"{{alliance}}"}}">
<legend>Autonomous</legend>
<div class="form-group">
<label class="col-lg-4 control-label">Robot Set</label>
<div class="col-lg-2"><input type="checkbox" class="input-sm" name="{{"{{alliance}}"}}AutoRobotSet"></div>
<label class="col-lg-4 control-label">Container Set</label>
<div class="col-lg-2"><input type="checkbox" class="input-sm" name="{{"{{alliance}}"}}AutoContainerSet"></div>
<label class="col-lg-4 control-label">Tote Set</label>
<div class="col-lg-2"><input type="checkbox" class="input-sm" name="{{"{{alliance}}"}}AutoToteSet"></div>
<label class="col-lg-4 control-label">Stacked Tote Set</label>
<div class="col-lg-2"><input type="checkbox" class="input-sm" name="{{"{{alliance}}"}}AutoStackedToteSet"></div>
<div class="col-lg-6">
<b>Defense Crossings</b><br />
<label class="col-lg-6 control-label">Position 1</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}AutoDefense1Crossings">
</div>
<label class="col-lg-6 control-label">Position 2</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}AutoDefense2Crossings">
</div>
<label class="col-lg-6 control-label">Position 3</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}AutoDefense3Crossings">
</div>
<label class="col-lg-6 control-label">Position 4</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}AutoDefense4Crossings">
</div>
<label class="col-lg-6 control-label">Position 5</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}AutoDefense5Crossings">
</div>
</div>
<div class="col-lg-6">
<br />
<div class="row">
<label class="col-lg-8 control-label">Defenses Reached</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}AutoDefensesReached">
</div>
</div>
<div class="row">
<label class="col-lg-8 control-label">High Goals</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}AutoHighGoals">
</div>
</div>
<div class="row">
<label class="col-lg-8 control-label">Low Goals</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}AutoLowGoals">
</div>
</div>
</div>
</div>
<legend>Teleoperated</legend>
<div class="form-group">
<label class="col-lg-4 control-label">Co-op Set</label>
<div class="col-lg-2"><input type="checkbox" class="input-sm" name="{{"{{alliance}}"}}CoopertitionSet"></div>
<label class="col-lg-4 control-label">Co-op Stack</label>
<div class="col-lg-2"><input type="checkbox" class="input-sm" name="{{"{{alliance}}"}}CoopertitionStack"></div>
</div>
{{"{{#each score.Stacks}}"}}
<div class="well well-sm well-dark{{"{{../alliance}}"}}">
<b id="{{"{{../alliance}}"}}Stack{{"{{@index}}"}}Title">Stack</b>
<button type="button" class="close" onclick="deleteStack('{{"{{../alliance}}"}}', {{"{{@index}}"}});">×</button>
<br />
<div class="form-group">
<label class="col-lg-2 control-label"># Totes</label>
<div class="col-lg-2"><input type="text" class="form-control input-sm" name="{{"{{../alliance}}"}}Stack{{"{{@index}}"}}Totes"></div>
<label class="col-lg-2 control-label">Container</label>
<div class="col-lg-2"><input type="checkbox" class="input-sm" name="{{"{{../alliance}}"}}Stack{{"{{@index}}"}}Container"></div>
<label class="col-lg-2 control-label">Litter</label>
<div class="col-lg-2"><input type="checkbox" class="input-sm" name="{{"{{../alliance}}"}}Stack{{"{{@index}}"}}Litter"></div>
<div class="col-lg-6">
<b>Defense Crossings</b><br />
<label class="col-lg-6 control-label">Position 1</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}Defense1Crossings">
</div>
<label class="col-lg-6 control-label">Position 2</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}Defense2Crossings">
</div>
<label class="col-lg-6 control-label">Position 3</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}Defense3Crossings">
</div>
<label class="col-lg-6 control-label">Position 4</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}Defense4Crossings">
</div>
<label class="col-lg-6 control-label">Position 5</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}Defense5Crossings">
</div>
</div>
{{"{{/each}}"}}
<button type="button" class="btn btn-default btn-sm" onclick="addStack('{{"{{alliance}}"}}');">
Add Stack
</button>
<div class="col-lg-6">
<br />
<div class="row">
<label class="col-lg-8 control-label">High Goals</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}HighGoals">
</div>
</div>
<div class="row">
<label class="col-lg-8 control-label">Low Goals</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}LowGoals">
</div>
</div>
<div class="row">
<label class="col-lg-8 control-label">Challenges</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}Challenges">
</div>
</div>
<div class="row">
<label class="col-lg-8 control-label">Scales</label>
<div class="col-lg-4">
<input type="text" class="form-control input-sm" name="{{"{{alliance}}"}}Scales">
</div>
</div>
</div>
</div>
<br /><br />
<legend>Fouls</legend>
{{"{{#each score.Fouls}}"}}
@@ -96,6 +160,12 @@
<input type="text" class="form-control input-sm" name="{{"{{../alliance}}"}}Foul{{"{{@index}}"}}Rule">
</div>
</div>
<div class="form-group">
<label class="col-lg-4 control-label">Is Technical</label>
<div class="col-lg-3">
<input type="checkbox" class="input-sm" name="{{"{{../alliance}}"}}Foul{{"{{@index}}"}}IsTechnical">
</div>
</div>
<div class="form-group">
<label class="col-lg-4 control-label">Seconds Into Match</label>
<div class="col-lg-3">