mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 21:56:50 -04:00
Add page to select playoff defenses.
This commit is contained in:
3
arena.go
3
arena.go
@@ -75,6 +75,7 @@ type Arena struct {
|
||||
allianceSelectionNotifier *Notifier
|
||||
lowerThirdNotifier *Notifier
|
||||
reloadDisplaysNotifier *Notifier
|
||||
defenseSelectionNotifier *Notifier
|
||||
audienceDisplayScreen string
|
||||
allianceStationDisplays map[string]string
|
||||
allianceStationDisplayScreen string
|
||||
@@ -123,6 +124,7 @@ func (arena *Arena) Setup() {
|
||||
arena.allianceSelectionNotifier = NewNotifier()
|
||||
arena.lowerThirdNotifier = NewNotifier()
|
||||
arena.reloadDisplaysNotifier = NewNotifier()
|
||||
arena.defenseSelectionNotifier = NewNotifier()
|
||||
|
||||
// Load empty match as current.
|
||||
arena.MatchState = PRE_MATCH
|
||||
@@ -226,6 +228,7 @@ func (arena *Arena) LoadMatch(match *Match) error {
|
||||
arena.realtimeScoreNotifier.Notify(nil)
|
||||
arena.allianceStationDisplayScreen = "match"
|
||||
arena.allianceStationDisplayNotifier.Notify(nil)
|
||||
arena.defenseSelectionNotifier.Notify(nil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,5 +19,5 @@ func TestFtaDisplay(t *testing.T) {
|
||||
|
||||
recorder := getHttpResponse("/displays/fta")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "FTA Display - Untitled Event - Cheesy Arena")
|
||||
assert.Contains(t, recorder.Body.String(), "Field Monitor - Untitled Event - Cheesy Arena")
|
||||
}
|
||||
|
||||
2
match.go
2
match.go
@@ -46,6 +46,8 @@ type Match struct {
|
||||
}
|
||||
|
||||
var placeableDefenses = []string{"CDF", "M", "R", "RW", "RT"}
|
||||
var defenseNames = map[string]string{"LB": "Low Bar", "CDF": "Cheval de Frise", "M": "Moat",
|
||||
"R": "Ramparts", "RW": "Rock Wall", "RT": "Rough Terrain"}
|
||||
|
||||
func (database *Database) CreateMatch(match *Match) error {
|
||||
return database.matchMap.Insert(match)
|
||||
|
||||
138
setup_defense_selection.go
Normal file
138
setup_defense_selection.go
Normal file
@@ -0,0 +1,138 @@
|
||||
// Copyright 2016 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Web routes for conducting the team defense selection process.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// Shows the defense selection page.
|
||||
func DefenseSelectionGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !UserIsAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
renderDefenseSelection(w, r, "")
|
||||
}
|
||||
|
||||
// Updates the cache with the latest input from the client.
|
||||
func DefenseSelectionPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !UserIsAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
matchId, _ := strconv.Atoi(r.PostFormValue("matchId"))
|
||||
match, err := db.GetMatchById(matchId)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
redErr := validateDefenseSelection([]string{r.PostFormValue("redDefense2"),
|
||||
r.PostFormValue("redDefense3"), r.PostFormValue("redDefense4"), r.PostFormValue("redDefense5")})
|
||||
if redErr == nil {
|
||||
match.RedDefense1 = "LB"
|
||||
match.RedDefense2 = r.PostFormValue("redDefense2")
|
||||
match.RedDefense3 = r.PostFormValue("redDefense3")
|
||||
match.RedDefense4 = r.PostFormValue("redDefense4")
|
||||
match.RedDefense5 = r.PostFormValue("redDefense5")
|
||||
}
|
||||
blueErr := validateDefenseSelection([]string{r.PostFormValue("blueDefense2"),
|
||||
r.PostFormValue("blueDefense3"), r.PostFormValue("blueDefense4"), r.PostFormValue("blueDefense5")})
|
||||
if blueErr == nil {
|
||||
match.BlueDefense1 = "LB"
|
||||
match.BlueDefense2 = r.PostFormValue("blueDefense2")
|
||||
match.BlueDefense3 = r.PostFormValue("blueDefense3")
|
||||
match.BlueDefense4 = r.PostFormValue("blueDefense4")
|
||||
match.BlueDefense5 = r.PostFormValue("blueDefense5")
|
||||
}
|
||||
if redErr == nil || blueErr == nil {
|
||||
err = db.SaveMatch(match)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
mainArena.defenseSelectionNotifier.Notify(nil)
|
||||
}
|
||||
if redErr != nil {
|
||||
renderDefenseSelection(w, r, redErr.Error())
|
||||
return
|
||||
}
|
||||
if blueErr != nil {
|
||||
renderDefenseSelection(w, r, blueErr.Error())
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/setup/defense_selection", 302)
|
||||
}
|
||||
|
||||
func renderDefenseSelection(w http.ResponseWriter, r *http.Request, errorMessage string) {
|
||||
template := template.New("").Funcs(templateHelpers)
|
||||
_, err := template.ParseFiles("templates/setup_defense_selection.html")
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
matches, err := db.GetMatchesByType("elimination")
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
var unplayedMatches []Match
|
||||
for _, match := range matches {
|
||||
if match.Status != "complete" {
|
||||
unplayedMatches = append(unplayedMatches, match)
|
||||
}
|
||||
}
|
||||
|
||||
data := struct {
|
||||
*EventSettings
|
||||
Matches []Match
|
||||
DefenseNames map[string]string
|
||||
ErrorMessage string
|
||||
}{eventSettings, unplayedMatches, defenseNames, errorMessage}
|
||||
err = template.ExecuteTemplate(w, "setup_defense_selection.html", data)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Takes a slice of the defenses in positions 2-5 and returns an error if they are not valid.
|
||||
func validateDefenseSelection(defenses []string) error {
|
||||
// Build map to track which defenses have been used.
|
||||
defenseCounts := make(map[string]int)
|
||||
for _, defense := range placeableDefenses {
|
||||
defenseCounts[defense] = 0
|
||||
}
|
||||
numBlankDefenses := 0
|
||||
|
||||
for _, defense := range defenses {
|
||||
if defense == "" {
|
||||
numBlankDefenses++
|
||||
continue
|
||||
}
|
||||
|
||||
defenseCount, ok := defenseCounts[defense]
|
||||
if !ok {
|
||||
return fmt.Errorf("Invalid defense type: %s", defense)
|
||||
}
|
||||
if defenseCount != 0 {
|
||||
return fmt.Errorf("Defense used more than once: %s", defense)
|
||||
}
|
||||
defenseCounts[defense]++
|
||||
}
|
||||
|
||||
if numBlankDefenses > 0 && numBlankDefenses < 4 {
|
||||
return fmt.Errorf("Cannot leave defenses blank.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -44,6 +44,7 @@
|
||||
<li><a href="/setup/alliance_selection">Alliance Selection</a></li>
|
||||
<li><a href="/setup/lower_thirds">Lower Thirds</a></li>
|
||||
<li><a href="/setup/sponsor_slides">Sponsor Slides</a></li>
|
||||
<li><a href="/setup/defense_selection">Playoff Defense Selection</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
@@ -84,7 +85,7 @@
|
||||
<li><a href="/displays/alliance_station">Alliance Station</a></li>
|
||||
<li><a href="/displays/announcer">Announcer</a></li>
|
||||
<li><a href="/displays/audience">Audience</a></li>
|
||||
<li><a href="/displays/fta">FTA</a></li>
|
||||
<li><a href="/displays/fta">Field Monitor</a></li>
|
||||
<li><a href="/displays/pit">Pit</a></li>
|
||||
<li><a href="/displays/referee">Referee</a></li>
|
||||
<li><a href="/displays/scoring/red">Scoring – Red</a></li>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Display showing team diagnostics for FTA/FTAA use.
|
||||
*/}}
|
||||
{{define "title"}}FTA Display{{end}}
|
||||
{{define "title"}}Field Monitor{{end}}
|
||||
{{define "body"}}
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
|
||||
@@ -153,7 +153,6 @@
|
||||
{{define "script"}}
|
||||
<script>
|
||||
$(function() {
|
||||
$("#displayBackgroundColor").colorpicker();
|
||||
var startTime = moment(new Date()).hour(13).minute(0).second(0);
|
||||
$("#startTimePicker").datetimepicker().data("DateTimePicker").setDate(startTime);
|
||||
});
|
||||
|
||||
80
templates/setup_defense_selection.html
Normal file
80
templates/setup_defense_selection.html
Normal file
@@ -0,0 +1,80 @@
|
||||
{{/*
|
||||
Copyright 2016 Team 254. All Rights Reserved.
|
||||
Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
|
||||
UI for controlling the team defense selection process.
|
||||
*/}}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Playoff Defense Selection - {{.EventSettings.Name}} - Cheesy Arena</title>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<link rel="shortcut icon" href="/static/img/favicon.ico">
|
||||
<link rel="apple-touch-icon" href="/static/img/apple-icon.png">
|
||||
<link href="/static/css/lib/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/css/cheesy-arena.css" rel="stylesheet">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<legend>Playoff Defense Selection</legend>
|
||||
{{if .ErrorMessage}}
|
||||
<div class="alert alert-dismissable alert-danger">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
{{.ErrorMessage}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{range $match := .Matches}}
|
||||
<div class="well">
|
||||
<form method="POST">
|
||||
<input type="hidden" name="matchId" value="{{$match.Id}}" />
|
||||
<legend>{{$match.DisplayName}}</legend>
|
||||
<div class="row well well-darkred">
|
||||
<div class="col-lg-2"><h4>{{$match.Red1}}, {{$match.Red2}}, {{$match.Red3}}</h4></div>
|
||||
<div class="col-lg-2">
|
||||
<select class="form-control" disabled="true">
|
||||
<option value="LB">{{index $.DefenseNames "LB"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
{{template "defense" dict "name" "redDefense2" "value" $match.RedDefense2 "defenseNames" $.DefenseNames}}
|
||||
{{template "defense" dict "name" "redDefense3" "value" $match.RedDefense3 "defenseNames" $.DefenseNames}}
|
||||
{{template "defense" dict "name" "redDefense4" "value" $match.RedDefense4 "defenseNames" $.DefenseNames}}
|
||||
{{template "defense" dict "name" "redDefense5" "value" $match.RedDefense5 "defenseNames" $.DefenseNames}}
|
||||
</div>
|
||||
<div class="row well well-darkblue">
|
||||
<div class="col-lg-2"><h4>{{$match.Blue1}}, {{$match.Blue2}}, {{$match.Blue3}}</h4></div>
|
||||
<div class="col-lg-2">
|
||||
<select class="form-control" disabled="true">
|
||||
<option value="LB">{{index $.DefenseNames "LB"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
{{template "defense" dict "name" "blueDefense2" "value" $match.BlueDefense2 "defenseNames" $.DefenseNames}}
|
||||
{{template "defense" dict "name" "blueDefense3" "value" $match.BlueDefense3 "defenseNames" $.DefenseNames}}
|
||||
{{template "defense" dict "name" "blueDefense4" "value" $match.BlueDefense4 "defenseNames" $.DefenseNames}}
|
||||
{{template "defense" dict "name" "blueDefense5" "value" $match.BlueDefense5 "defenseNames" $.DefenseNames}}
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<button type="submit" class="btn btn-danger">Save Selections</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<script src="/static/js/lib/bootstrap.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{define "defense"}}
|
||||
<div class="col-lg-2">
|
||||
<select class="form-control" name="{{.name}}">
|
||||
<option value=""></option>
|
||||
<option value="CDF"{{if eq .value "CDF"}} selected{{end}}>{{index .defenseNames "CDF"}}</option>
|
||||
<option value="M"{{if eq .value "M"}} selected{{end}}>{{index .defenseNames "M"}}</option>
|
||||
<option value="R"{{if eq .value "R"}} selected{{end}}>{{index .defenseNames "R"}}</option>
|
||||
<option value="RW"{{if eq .value "RW"}} selected{{end}}>{{index .defenseNames "RW"}}</option>
|
||||
<option value="RT"{{if eq .value "RT"}} selected{{end}}>{{index .defenseNames "RT"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
{{end}}
|
||||
2
web.go
2
web.go
@@ -182,6 +182,8 @@ func newHandler() http.Handler {
|
||||
router.HandleFunc("/setup/sponsor_slides", SponsorSlidesGetHandler).Methods("GET")
|
||||
router.HandleFunc("/setup/sponsor_slides", SponsorSlidesPostHandler).Methods("POST")
|
||||
router.HandleFunc("/api/sponsor_slides", SponsorSlidesApiHandler).Methods("GET")
|
||||
router.HandleFunc("/setup/defense_selection", DefenseSelectionGetHandler).Methods("GET")
|
||||
router.HandleFunc("/setup/defense_selection", DefenseSelectionPostHandler).Methods("POST")
|
||||
router.HandleFunc("/match_play", MatchPlayHandler).Methods("GET")
|
||||
router.HandleFunc("/match_play/{matchId}/load", MatchPlayLoadHandler).Methods("GET")
|
||||
router.HandleFunc("/match_play/{matchId}/show_result", MatchPlayShowResultHandler).Methods("GET")
|
||||
|
||||
Reference in New Issue
Block a user