mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 13:46:44 -04:00
Add defense randomization and schedule report.
This commit is contained in:
2
match.go
2
match.go
@@ -45,6 +45,8 @@ type Match struct {
|
||||
BlueDefense5 string
|
||||
}
|
||||
|
||||
var placeableDefenses = []string{"CDF", "M", "R", "RW", "RT"}
|
||||
|
||||
func (database *Database) CreateMatch(match *Match) error {
|
||||
return database.matchMap.Insert(match)
|
||||
}
|
||||
|
||||
62
reports.go
62
reports.go
@@ -235,6 +235,68 @@ func SchedulePdfReportHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Generates a PDF-formatted report of the defenses schedule.
|
||||
func DefensesPdfReportHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !UserIsReader(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
matches, err := db.GetMatchesByType(vars["type"])
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// The widths of the table columns in mm, stored here so that they can be referenced for each row.
|
||||
colWidths := map[string]float64{"Type": 25, "Match": 15, "Defense": 15.5}
|
||||
rowHeight := 6.5
|
||||
|
||||
pdf := gofpdf.New("P", "mm", "Letter", "font")
|
||||
pdf.AddPage()
|
||||
|
||||
// Render table header row.
|
||||
pdf.SetFont("Arial", "B", 10)
|
||||
pdf.SetFillColor(220, 220, 220)
|
||||
pdf.CellFormat(195, rowHeight, "Defenses Schedule - "+eventSettings.Name, "", 1, "C", false, 0, "")
|
||||
pdf.CellFormat(colWidths["Type"], rowHeight, "Type", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["Match"], rowHeight, "Match", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, "Red 1", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, "Red 2", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, "Red 3", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, "Red 4", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, "Red 5", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, "Blue 1", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, "Blue 2", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, "Blue 3", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, "Blue 4", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, "Blue 5", "1", 1, "C", true, 0, "")
|
||||
pdf.SetFont("Arial", "", 10)
|
||||
for _, match := range matches {
|
||||
// Render match defenses row.
|
||||
pdf.CellFormat(colWidths["Type"], rowHeight, match.CapitalizedType(), "1", 0, "C", false, 0, "")
|
||||
pdf.CellFormat(colWidths["Match"], rowHeight, match.DisplayName, "1", 0, "C", false, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, match.RedDefense1, "1", 0, "C", false, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, match.RedDefense2, "1", 0, "C", false, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, match.RedDefense3, "1", 0, "C", false, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, match.RedDefense4, "1", 0, "C", false, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, match.RedDefense5, "1", 0, "C", false, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, match.BlueDefense1, "1", 0, "C", false, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, match.BlueDefense2, "1", 0, "C", false, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, match.BlueDefense3, "1", 0, "C", false, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, match.BlueDefense4, "1", 0, "C", false, 0, "")
|
||||
pdf.CellFormat(colWidths["Defense"], rowHeight, match.BlueDefense5, "1", 1, "C", false, 0, "")
|
||||
}
|
||||
|
||||
// Write out the PDF file as the HTTP response.
|
||||
w.Header().Set("Content-Type", "application/pdf")
|
||||
err = pdf.Output(w)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Generates a CSV-formatted report of the team list.
|
||||
func TeamsCsvReportHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !UserIsReader(w, r) {
|
||||
|
||||
@@ -60,9 +60,11 @@ func TestScheduleCsvReport(t *testing.T) {
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Equal(t, "text/plain", recorder.HeaderMap["Content-Type"][0])
|
||||
expectedBody := "Match,Type,Time,Red1,Red1IsSurrogate,Red2,Red2IsSurrogate,Red3,Red3IsSurrogate,Blue1," +
|
||||
"Blue1IsSurrogate,Blue2,Blue2IsSurrogate,Blue3,Blue3IsSurrogate\n1,qualification," +
|
||||
"1969-12-31 16:00:00 -0800 PST,1,false,2,false,3,false,4,true,5,true,6,true\n" +
|
||||
"2,qualification,1969-12-31 16:10:00 -0800 PST,7,true,8,true,9,true,10,false,11,false,12,false\n\n"
|
||||
"Blue1IsSurrogate,Blue2,Blue2IsSurrogate,Blue3,Blue3IsSurrogate,RedDefense1,RedDefense2," +
|
||||
"RedDefense3,RedDefense4,RedDefense5,BlueDefense1,BlueDefense2,BlueDefense3,BlueDefense4," +
|
||||
"BlueDefense5\n1,qualification,1969-12-31 16:00:00 -0800 PST,1,false,2,false,3,false,4,true,5,true," +
|
||||
"6,true,,,,,,,,,,\n2,qualification,1969-12-31 16:10:00 -0800 PST,7,true,8,true,9,true,10,false,11,false,12," +
|
||||
"false,,,,,,,,,,\n\n"
|
||||
assert.Equal(t, expectedBody, recorder.Body.String())
|
||||
}
|
||||
|
||||
|
||||
27
schedule.go
27
schedule.go
@@ -88,6 +88,8 @@ func BuildRandomSchedule(teams []Team, scheduleBlocks []ScheduleBlock, matchType
|
||||
}
|
||||
}
|
||||
|
||||
randomizeDefenses(matches, numTeams)
|
||||
|
||||
return matches, nil
|
||||
}
|
||||
|
||||
@@ -99,3 +101,28 @@ func countMatches(scheduleBlocks []ScheduleBlock) int {
|
||||
}
|
||||
return numMatches
|
||||
}
|
||||
|
||||
// Fills in a random set of defenses per round of all teams playing.
|
||||
func randomizeDefenses(matches []Match, numTeams int) {
|
||||
// Take the floor, to err on the side of a team missing a set of defenses instead of seeing it twice.
|
||||
matchesPerRound := numTeams / 6
|
||||
|
||||
var defenseShuffle []int
|
||||
for i := 0; i < len(matches); i++ {
|
||||
if i%matchesPerRound == 0 {
|
||||
// Pick a new set of defenses.
|
||||
defenseShuffle = rand.Perm(len(placeableDefenses))
|
||||
}
|
||||
|
||||
matches[i].RedDefense1 = "LB"
|
||||
matches[i].RedDefense2 = placeableDefenses[defenseShuffle[0]]
|
||||
matches[i].RedDefense3 = placeableDefenses[defenseShuffle[1]]
|
||||
matches[i].RedDefense4 = placeableDefenses[defenseShuffle[2]]
|
||||
matches[i].RedDefense5 = placeableDefenses[defenseShuffle[3]]
|
||||
matches[i].BlueDefense1 = "LB"
|
||||
matches[i].BlueDefense2 = placeableDefenses[defenseShuffle[0]]
|
||||
matches[i].BlueDefense3 = placeableDefenses[defenseShuffle[1]]
|
||||
matches[i].BlueDefense4 = placeableDefenses[defenseShuffle[2]]
|
||||
matches[i].BlueDefense5 = placeableDefenses[defenseShuffle[3]]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,17 +56,29 @@ func TestScheduleTeams(t *testing.T) {
|
||||
matches, err := BuildRandomSchedule(teams, scheduleBlocks, "test")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, Match{Type: "test", DisplayName: "1", Time: time.Unix(0, 0).UTC(), Red1: 115, Red2: 111,
|
||||
Red3: 108, Blue1: 109, Blue2: 116, Blue3: 117}, matches[0])
|
||||
Red3: 108, Blue1: 109, Blue2: 116, Blue3: 117, RedDefense1: "LB", RedDefense2: "RW", RedDefense3: "RT",
|
||||
RedDefense4: "R", RedDefense5: "M", BlueDefense1: "LB", BlueDefense2: "RW", BlueDefense3: "RT",
|
||||
BlueDefense4: "R", BlueDefense5: "M"}, matches[0])
|
||||
assert.Equal(t, Match{Type: "test", DisplayName: "2", Time: time.Unix(60, 0).UTC(), Red1: 114, Red2: 112,
|
||||
Red3: 103, Blue1: 101, Blue2: 104, Blue3: 118}, matches[1])
|
||||
Red3: 103, Blue1: 101, Blue2: 104, Blue3: 118, RedDefense1: "LB", RedDefense2: "RW", RedDefense3: "RT",
|
||||
RedDefense4: "R", RedDefense5: "M", BlueDefense1: "LB", BlueDefense2: "RW", BlueDefense3: "RT",
|
||||
BlueDefense4: "R", BlueDefense5: "M"}, matches[1])
|
||||
assert.Equal(t, Match{Type: "test", DisplayName: "3", Time: time.Unix(120, 0).UTC(), Red1: 110, Red2: 107,
|
||||
Red3: 105, Blue1: 106, Blue2: 113, Blue3: 102}, matches[2])
|
||||
Red3: 105, Blue1: 106, Blue2: 113, Blue3: 102, RedDefense1: "LB", RedDefense2: "RW", RedDefense3: "RT",
|
||||
RedDefense4: "R", RedDefense5: "M", BlueDefense1: "LB", BlueDefense2: "RW", BlueDefense3: "RT",
|
||||
BlueDefense4: "R", BlueDefense5: "M"}, matches[2])
|
||||
assert.Equal(t, Match{Type: "test", DisplayName: "4", Time: time.Unix(180, 0).UTC(), Red1: 112, Red2: 108,
|
||||
Red3: 109, Blue1: 101, Blue2: 111, Blue3: 103}, matches[3])
|
||||
Red3: 109, Blue1: 101, Blue2: 111, Blue3: 103, RedDefense1: "LB", RedDefense2: "RT", RedDefense3: "M",
|
||||
RedDefense4: "CDF", RedDefense5: "R", BlueDefense1: "LB", BlueDefense2: "RT", BlueDefense3: "M",
|
||||
BlueDefense4: "CDF", BlueDefense5: "R"}, matches[3])
|
||||
assert.Equal(t, Match{Type: "test", DisplayName: "5", Time: time.Unix(240, 0).UTC(), Red1: 113, Red2: 117,
|
||||
Red3: 115, Blue1: 110, Blue2: 114, Blue3: 102}, matches[4])
|
||||
Red3: 115, Blue1: 110, Blue2: 114, Blue3: 102, RedDefense1: "LB", RedDefense2: "RT", RedDefense3: "M",
|
||||
RedDefense4: "CDF", RedDefense5: "R", BlueDefense1: "LB", BlueDefense2: "RT", BlueDefense3: "M",
|
||||
BlueDefense4: "CDF", BlueDefense5: "R"}, matches[4])
|
||||
assert.Equal(t, Match{Type: "test", DisplayName: "6", Time: time.Unix(300, 0).UTC(), Red1: 118, Red2: 105,
|
||||
Red3: 106, Blue1: 107, Blue2: 104, Blue3: 116}, matches[5])
|
||||
Red3: 106, Blue1: 107, Blue2: 104, Blue3: 116, RedDefense1: "LB", RedDefense2: "RT", RedDefense3: "M",
|
||||
RedDefense4: "CDF", RedDefense5: "R", BlueDefense1: "LB", BlueDefense2: "RT", BlueDefense3: "M",
|
||||
BlueDefense4: "CDF", BlueDefense5: "R"}, matches[5])
|
||||
|
||||
// Check with excess room for matches in the schedule.
|
||||
scheduleBlocks = []ScheduleBlock{{time.Unix(0, 0).UTC(), 7, 60}}
|
||||
|
||||
@@ -60,8 +60,11 @@
|
||||
<li class="dropdown-header">PDF Reports</li>
|
||||
<li><a target="_blank" href="/reports/pdf/teams">Team List</a></li>
|
||||
<li><a target="_blank" href="/reports/pdf/schedule/practice">Practice Schedule</a></li>
|
||||
<li><a target="_blank" href="/reports/pdf/defenses/practice">Practice Defenses</a></li>
|
||||
<li><a target="_blank" href="/reports/pdf/schedule/qualification">Qualification Schedule</a></li>
|
||||
<li><a target="_blank" href="/reports/pdf/defenses/qualification">Qualification Defenses</a></li>
|
||||
<li><a target="_blank" href="/reports/pdf/schedule/elimination">Playoff Schedule</a></li>
|
||||
<li><a target="_blank" href="/reports/pdf/defenses/elimination">Playoff Defenses</a></li>
|
||||
<li><a target="_blank" href="/reports/pdf/rankings">Standings</a></li>
|
||||
<li class="divider"></li>
|
||||
<li class="dropdown-header">CSV Data Export</li>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
Match,Type,Time,Red1,Red1IsSurrogate,Red2,Red2IsSurrogate,Red3,Red3IsSurrogate,Blue1,Blue1IsSurrogate,Blue2,Blue2IsSurrogate,Blue3,Blue3IsSurrogate
|
||||
{{range $match := .}}{{$match.DisplayName}},{{$match.Type}},{{$match.Time.Local}},{{$match.Red1}},{{$match.Red1IsSurrogate}},{{$match.Red2}},{{$match.Red2IsSurrogate}},{{$match.Red3}},{{$match.Red3IsSurrogate}},{{$match.Blue1}},{{$match.Blue1IsSurrogate}},{{$match.Blue2}},{{$match.Blue2IsSurrogate}},{{$match.Blue3}},{{$match.Blue3IsSurrogate}}
|
||||
Match,Type,Time,Red1,Red1IsSurrogate,Red2,Red2IsSurrogate,Red3,Red3IsSurrogate,Blue1,Blue1IsSurrogate,Blue2,Blue2IsSurrogate,Blue3,Blue3IsSurrogate,RedDefense1,RedDefense2,RedDefense3,RedDefense4,RedDefense5,BlueDefense1,BlueDefense2,BlueDefense3,BlueDefense4,BlueDefense5
|
||||
{{range $match := .}}{{$match.DisplayName}},{{$match.Type}},{{$match.Time.Local}},{{$match.Red1}},{{$match.Red1IsSurrogate}},{{$match.Red2}},{{$match.Red2IsSurrogate}},{{$match.Red3}},{{$match.Red3IsSurrogate}},{{$match.Blue1}},{{$match.Blue1IsSurrogate}},{{$match.Blue2}},{{$match.Blue2IsSurrogate}},{{$match.Blue3}},{{$match.Blue3IsSurrogate}},{{$match.RedDefense1}},{{$match.RedDefense2}},{{$match.RedDefense3}},{{$match.RedDefense4}},{{$match.RedDefense5}},{{$match.BlueDefense1}},{{$match.BlueDefense2}},{{$match.BlueDefense3}},{{$match.BlueDefense4}},{{$match.BlueDefense5}}
|
||||
{{end}}
|
||||
|
||||
|
1
web.go
1
web.go
@@ -193,6 +193,7 @@ func newHandler() http.Handler {
|
||||
router.HandleFunc("/reports/pdf/rankings", RankingsPdfReportHandler).Methods("GET")
|
||||
router.HandleFunc("/reports/csv/schedule/{type}", ScheduleCsvReportHandler).Methods("GET")
|
||||
router.HandleFunc("/reports/pdf/schedule/{type}", SchedulePdfReportHandler).Methods("GET")
|
||||
router.HandleFunc("/reports/pdf/defenses/{type}", DefensesPdfReportHandler).Methods("GET")
|
||||
router.HandleFunc("/reports/csv/teams", TeamsCsvReportHandler).Methods("GET")
|
||||
router.HandleFunc("/reports/pdf/teams", TeamsPdfReportHandler).Methods("GET")
|
||||
router.HandleFunc("/reports/csv/wpa_keys", WpaKeysCsvReportHandler).Methods("GET")
|
||||
|
||||
Reference in New Issue
Block a user