mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 13:46:44 -04:00
Persist schedule blocks to the DB.
This commit is contained in:
11
db/migrations/20180824200145_CreateScheduleBlocks.sql
Normal file
11
db/migrations/20180824200145_CreateScheduleBlocks.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- +goose Up
|
||||||
|
CREATE TABLE schedule_blocks (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
matchtype VARCHAR(16),
|
||||||
|
starttime DATETIME,
|
||||||
|
nummatches int,
|
||||||
|
matchspacingsec int
|
||||||
|
);
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
DROP TABLE schedule_blocks;
|
||||||
@@ -531,7 +531,6 @@ func (arena *Arena) Update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle field sensors/lights/motors.
|
// Handle field sensors/lights/motors.
|
||||||
//arena.Plc.SimulateInput(matchTimeSec)
|
|
||||||
arena.handlePlcInput()
|
arena.handlePlcInput()
|
||||||
arena.handlePlcOutput()
|
arena.handlePlcOutput()
|
||||||
arena.handleLeds()
|
arena.handleLeds()
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ type Database struct {
|
|||||||
allianceTeamMap *modl.DbMap
|
allianceTeamMap *modl.DbMap
|
||||||
lowerThirdMap *modl.DbMap
|
lowerThirdMap *modl.DbMap
|
||||||
sponsorSlideMap *modl.DbMap
|
sponsorSlideMap *modl.DbMap
|
||||||
|
scheduleBlockMap *modl.DbMap
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opens the SQLite database at the given path, creating it if it doesn't exist, and runs any pending
|
// Opens the SQLite database at the given path, creating it if it doesn't exist, and runs any pending
|
||||||
@@ -120,6 +121,9 @@ func (database *Database) mapTables() {
|
|||||||
|
|
||||||
database.sponsorSlideMap = modl.NewDbMap(database.db, dialect)
|
database.sponsorSlideMap = modl.NewDbMap(database.db, dialect)
|
||||||
database.sponsorSlideMap.AddTableWithName(SponsorSlide{}, "sponsor_slides").SetKeys(true, "Id")
|
database.sponsorSlideMap.AddTableWithName(SponsorSlide{}, "sponsor_slides").SetKeys(true, "Id")
|
||||||
|
|
||||||
|
database.scheduleBlockMap = modl.NewDbMap(database.db, dialect)
|
||||||
|
database.scheduleBlockMap.AddTableWithName(ScheduleBlock{}, "schedule_blocks").SetKeys(true, "Id")
|
||||||
}
|
}
|
||||||
|
|
||||||
func serializeHelper(target *string, source interface{}) error {
|
func serializeHelper(target *string, source interface{}) error {
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func (database *Database) TruncateMatches() error {
|
|||||||
|
|
||||||
func (database *Database) GetMatchByName(matchType string, displayName string) (*Match, error) {
|
func (database *Database) GetMatchByName(matchType string, displayName string) (*Match, error) {
|
||||||
var matches []Match
|
var matches []Match
|
||||||
err := database.teamMap.Select(&matches, "SELECT * FROM matches WHERE type = ? AND displayname = ?",
|
err := database.matchMap.Select(&matches, "SELECT * FROM matches WHERE type = ? AND displayname = ?",
|
||||||
matchType, displayName)
|
matchType, displayName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -82,14 +82,14 @@ func (database *Database) GetMatchByName(matchType string, displayName string) (
|
|||||||
|
|
||||||
func (database *Database) GetMatchesByElimRoundGroup(round int, group int) ([]Match, error) {
|
func (database *Database) GetMatchesByElimRoundGroup(round int, group int) ([]Match, error) {
|
||||||
var matches []Match
|
var matches []Match
|
||||||
err := database.teamMap.Select(&matches, "SELECT * FROM matches WHERE type = 'elimination' AND "+
|
err := database.matchMap.Select(&matches, "SELECT * FROM matches WHERE type = 'elimination' AND "+
|
||||||
"elimround = ? AND elimgroup = ? ORDER BY eliminstance", round, group)
|
"elimround = ? AND elimgroup = ? ORDER BY eliminstance", round, group)
|
||||||
return matches, err
|
return matches, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (database *Database) GetMatchesByType(matchType string) ([]Match, error) {
|
func (database *Database) GetMatchesByType(matchType string) ([]Match, error) {
|
||||||
var matches []Match
|
var matches []Match
|
||||||
err := database.teamMap.Select(&matches,
|
err := database.matchMap.Select(&matches,
|
||||||
"SELECT * FROM matches WHERE type = ? ORDER BY elimround desc, eliminstance, elimgroup, id", matchType)
|
"SELECT * FROM matches WHERE type = ? ORDER BY elimround desc, eliminstance, elimgroup, id", matchType)
|
||||||
return matches, err
|
return matches, err
|
||||||
}
|
}
|
||||||
|
|||||||
38
model/schedule_block.go
Normal file
38
model/schedule_block.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2018 Team 254. All Rights Reserved.
|
||||||
|
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||||
|
//
|
||||||
|
// Model and datastore CRUD methods for a schedule block at an event.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ScheduleBlock struct {
|
||||||
|
Id int
|
||||||
|
MatchType string
|
||||||
|
StartTime time.Time
|
||||||
|
NumMatches int
|
||||||
|
MatchSpacingSec int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (database *Database) CreateScheduleBlock(block *ScheduleBlock) error {
|
||||||
|
return database.scheduleBlockMap.Insert(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (database *Database) GetScheduleBlocksByMatchType(matchType string) ([]ScheduleBlock, error) {
|
||||||
|
var blocks []ScheduleBlock
|
||||||
|
err := database.scheduleBlockMap.Select(&blocks, "SELECT * FROM schedule_blocks WHERE matchtype = ? ORDER BY "+
|
||||||
|
"starttime ", matchType)
|
||||||
|
return blocks, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (database *Database) DeleteScheduleBlocksByMatchType(matchType string) error {
|
||||||
|
_, err := database.scheduleBlockMap.Exec("DELETE FROM schedule_blocks WHERE matchtype = ?", matchType)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (database *Database) TruncateScheduleBlocks() error {
|
||||||
|
return database.scheduleBlockMap.TruncateTables()
|
||||||
|
}
|
||||||
47
model/schedule_block_test.go
Normal file
47
model/schedule_block_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2018 Team 254. All Rights Reserved.
|
||||||
|
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestScheduleBlockCrud(t *testing.T) {
|
||||||
|
db := setupTestDb(t)
|
||||||
|
|
||||||
|
scheduleBlock1 := ScheduleBlock{0, "practice", time.Now().UTC(), 10, 600}
|
||||||
|
assert.Nil(t, db.CreateScheduleBlock(&scheduleBlock1))
|
||||||
|
scheduleBlock2 := ScheduleBlock{0, "qualification", time.Now().UTC(), 20, 480}
|
||||||
|
assert.Nil(t, db.CreateScheduleBlock(&scheduleBlock2))
|
||||||
|
scheduleBlock3 := ScheduleBlock{0, "qualification", scheduleBlock2.StartTime.Add(time.Second * 20 * 480), 20, 480}
|
||||||
|
assert.Nil(t, db.CreateScheduleBlock(&scheduleBlock3))
|
||||||
|
|
||||||
|
// Test retrieval of all blocks by match type.
|
||||||
|
blocks, err := db.GetScheduleBlocksByMatchType("practice")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
if assert.Equal(t, 1, len(blocks)) {
|
||||||
|
assert.Equal(t, scheduleBlock1, blocks[0])
|
||||||
|
}
|
||||||
|
blocks, err = db.GetScheduleBlocksByMatchType("qualification")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
if assert.Equal(t, 2, len(blocks)) {
|
||||||
|
assert.Equal(t, scheduleBlock2, blocks[0])
|
||||||
|
assert.Equal(t, scheduleBlock3, blocks[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test deletion of blocks.
|
||||||
|
assert.Nil(t, db.DeleteScheduleBlocksByMatchType("practice"))
|
||||||
|
blocks, err = db.GetScheduleBlocksByMatchType("practice")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 0, len(blocks))
|
||||||
|
blocks, err = db.GetScheduleBlocksByMatchType("qualification")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 2, len(blocks))
|
||||||
|
assert.Nil(t, db.TruncateScheduleBlocks())
|
||||||
|
blocks, err = db.GetScheduleBlocksByMatchType("qualification")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 0, len(blocks))
|
||||||
|
}
|
||||||
@@ -44,6 +44,6 @@ func (database *Database) TruncateSponsorSlides() error {
|
|||||||
|
|
||||||
func (database *Database) GetAllSponsorSlides() ([]SponsorSlide, error) {
|
func (database *Database) GetAllSponsorSlides() ([]SponsorSlide, error) {
|
||||||
var sponsorSlides []SponsorSlide
|
var sponsorSlides []SponsorSlide
|
||||||
err := database.teamMap.Select(&sponsorSlides, "SELECT * FROM sponsor_slides ORDER BY id")
|
err := database.sponsorSlideMap.Select(&sponsorSlides, "SELECT * FROM sponsor_slides ORDER BY id")
|
||||||
return sponsorSlides, err
|
return sponsorSlides, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
<div class="col-lg-5">
|
<div class="col-lg-5">
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<form id="scheduleForm" class="form-horizontal" action="/setup/schedule/save" method="POST">
|
<form id="scheduleForm" class="form-horizontal" action="/setup/schedule/save?matchType={{.MatchType}}"
|
||||||
|
method="POST">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Schedule Parameters</legend>
|
<legend>Schedule Parameters</legend>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -24,14 +25,16 @@
|
|||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="matchType" value="practice"
|
<input type="radio" name="matchType" value="practice"
|
||||||
{{if eq .MatchType "practice"}}checked{{end}}>
|
onchange="window.location = '/setup/schedule?matchType=practice';"
|
||||||
|
{{if eq .MatchType "practice"}}checked{{end}}>
|
||||||
Practice
|
Practice
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="matchType" value="qualification"
|
<input type="radio" name="matchType" value="qualification"
|
||||||
{{if eq .MatchType "qualification"}}checked{{end}}>
|
onchange="window.location = '/setup/schedule?matchType=qualification';"
|
||||||
|
{{if eq .MatchType "qualification"}}checked{{end}}>
|
||||||
Qualification
|
Qualification
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -46,11 +49,11 @@
|
|||||||
</p>
|
</p>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<button type="button" class="btn btn-default" onclick="addBlock();">Add Block</button>
|
<p><button type="button" class="btn btn-default" onclick="addBlock();">Add Block</button>
|
||||||
<button type="button" class="btn btn-info" onclick="generateSchedule();">
|
<button type="button" class="btn btn-info" onclick="generateSchedule();">
|
||||||
Generate Schedule
|
Generate Schedule/Save Blocks
|
||||||
</button>
|
</button></p>
|
||||||
<button type="submit" class="btn btn-primary">Save Schedule</button>
|
<p><button type="submit" class="btn btn-primary">Save Schedule</button></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{if .EventSettings.TbaPublishingEnabled}}
|
{{if .EventSettings.TbaPublishingEnabled}}
|
||||||
@@ -156,7 +159,7 @@
|
|||||||
existing matches and their data.</p>
|
existing matches and their data.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<form class="form-horizontal" action="/setup/schedule/republish" method="POST">
|
<form class="form-horizontal" action="/setup/schedule/republish?matchType={{.MatchType}}" method="POST">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
<button type="submit" class="btn btn-primary">Publish Schedule</button>
|
<button type="submit" class="btn btn-primary">Publish Schedule</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -22,14 +22,9 @@ const (
|
|||||||
TeamsPerMatch = 6
|
TeamsPerMatch = 6
|
||||||
)
|
)
|
||||||
|
|
||||||
type ScheduleBlock struct {
|
|
||||||
StartTime time.Time
|
|
||||||
NumMatches int
|
|
||||||
MatchSpacingSec int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a random schedule for the given parameters and returns it as a list of matches.
|
// Creates a random schedule for the given parameters and returns it as a list of matches.
|
||||||
func BuildRandomSchedule(teams []model.Team, scheduleBlocks []ScheduleBlock, matchType string) ([]model.Match, error) {
|
func BuildRandomSchedule(teams []model.Team, scheduleBlocks []model.ScheduleBlock,
|
||||||
|
matchType string) ([]model.Match, error) {
|
||||||
// Load the anonymized, pre-randomized match schedule for the given number of teams and matches per team.
|
// Load the anonymized, pre-randomized match schedule for the given number of teams and matches per team.
|
||||||
numTeams := len(teams)
|
numTeams := len(teams)
|
||||||
numMatches := countMatches(scheduleBlocks)
|
numMatches := countMatches(scheduleBlocks)
|
||||||
@@ -97,7 +92,7 @@ func BuildRandomSchedule(teams []model.Team, scheduleBlocks []ScheduleBlock, mat
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns the total number of matches that can be run within the given schedule blocks.
|
// Returns the total number of matches that can be run within the given schedule blocks.
|
||||||
func countMatches(scheduleBlocks []ScheduleBlock) int {
|
func countMatches(scheduleBlocks []model.ScheduleBlock) int {
|
||||||
numMatches := 0
|
numMatches := 0
|
||||||
for _, block := range scheduleBlocks {
|
for _, block := range scheduleBlocks {
|
||||||
numMatches += block.NumMatches
|
numMatches += block.NumMatches
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func TestMain(m *testing.M) {
|
|||||||
|
|
||||||
func TestNonExistentSchedule(t *testing.T) {
|
func TestNonExistentSchedule(t *testing.T) {
|
||||||
teams := make([]model.Team, 6)
|
teams := make([]model.Team, 6)
|
||||||
scheduleBlocks := []ScheduleBlock{{time.Unix(0, 0).UTC(), 2, 60}}
|
scheduleBlocks := []model.ScheduleBlock{{0, "", time.Unix(0, 0).UTC(), 2, 60}}
|
||||||
_, err := BuildRandomSchedule(teams, scheduleBlocks, "test")
|
_, err := BuildRandomSchedule(teams, scheduleBlocks, "test")
|
||||||
expectedErr := "No schedule template exists for 6 teams and 2 matches"
|
expectedErr := "No schedule template exists for 6 teams and 2 matches"
|
||||||
if assert.NotNil(t, err) {
|
if assert.NotNil(t, err) {
|
||||||
@@ -35,7 +35,7 @@ func TestMalformedSchedule(t *testing.T) {
|
|||||||
scheduleFile.WriteString("1,0,2,0,3,0,4,0,5,0,6,0\n6,0,5,0,4,0,3,0,2,0,1,0\n")
|
scheduleFile.WriteString("1,0,2,0,3,0,4,0,5,0,6,0\n6,0,5,0,4,0,3,0,2,0,1,0\n")
|
||||||
scheduleFile.Close()
|
scheduleFile.Close()
|
||||||
teams := make([]model.Team, 6)
|
teams := make([]model.Team, 6)
|
||||||
scheduleBlocks := []ScheduleBlock{{time.Unix(0, 0).UTC(), 1, 60}}
|
scheduleBlocks := []model.ScheduleBlock{{0, "", time.Unix(0, 0).UTC(), 1, 60}}
|
||||||
_, err := BuildRandomSchedule(teams, scheduleBlocks, "test")
|
_, err := BuildRandomSchedule(teams, scheduleBlocks, "test")
|
||||||
expectedErr := "Schedule file contains 2 matches, expected 1"
|
expectedErr := "Schedule file contains 2 matches, expected 1"
|
||||||
if assert.NotNil(t, err) {
|
if assert.NotNil(t, err) {
|
||||||
@@ -60,7 +60,7 @@ func TestScheduleTeams(t *testing.T) {
|
|||||||
for i := 0; i < numTeams; i++ {
|
for i := 0; i < numTeams; i++ {
|
||||||
teams[i].Id = i + 101
|
teams[i].Id = i + 101
|
||||||
}
|
}
|
||||||
scheduleBlocks := []ScheduleBlock{{time.Unix(0, 0).UTC(), 6, 60}}
|
scheduleBlocks := []model.ScheduleBlock{{0, "", time.Unix(0, 0).UTC(), 6, 60}}
|
||||||
matches, err := BuildRandomSchedule(teams, scheduleBlocks, "test")
|
matches, err := BuildRandomSchedule(teams, scheduleBlocks, "test")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, model.Match{Type: "test", DisplayName: "1", Time: time.Unix(0, 0).UTC(), Red1: 115, Red2: 111,
|
assert.Equal(t, model.Match{Type: "test", DisplayName: "1", Time: time.Unix(0, 0).UTC(), Red1: 115, Red2: 111,
|
||||||
@@ -77,16 +77,16 @@ func TestScheduleTeams(t *testing.T) {
|
|||||||
Red3: 106, Blue1: 107, Blue2: 104, Blue3: 116}, matches[5])
|
Red3: 106, Blue1: 107, Blue2: 104, Blue3: 116}, matches[5])
|
||||||
|
|
||||||
// Check with excess room for matches in the schedule.
|
// Check with excess room for matches in the schedule.
|
||||||
scheduleBlocks = []ScheduleBlock{{time.Unix(0, 0).UTC(), 7, 60}}
|
scheduleBlocks = []model.ScheduleBlock{{0, "", time.Unix(0, 0).UTC(), 7, 60}}
|
||||||
matches, err = BuildRandomSchedule(teams, scheduleBlocks, "test")
|
matches, err = BuildRandomSchedule(teams, scheduleBlocks, "test")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestScheduleTiming(t *testing.T) {
|
func TestScheduleTiming(t *testing.T) {
|
||||||
teams := make([]model.Team, 18)
|
teams := make([]model.Team, 18)
|
||||||
scheduleBlocks := []ScheduleBlock{{time.Unix(100, 0).UTC(), 10, 75},
|
scheduleBlocks := []model.ScheduleBlock{{0, "", time.Unix(100, 0).UTC(), 10, 75},
|
||||||
{time.Unix(20000, 0).UTC(), 5, 1000},
|
{0, "", time.Unix(20000, 0).UTC(), 5, 1000},
|
||||||
{time.Unix(100000, 0).UTC(), 15, 29}}
|
{0, "", time.Unix(100000, 0).UTC(), 15, 29}}
|
||||||
matches, err := BuildRandomSchedule(teams, scheduleBlocks, "test")
|
matches, err := BuildRandomSchedule(teams, scheduleBlocks, "test")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, time.Unix(100, 0).UTC(), matches[0].Time)
|
assert.Equal(t, time.Unix(100, 0).UTC(), matches[0].Time)
|
||||||
@@ -105,7 +105,7 @@ func TestScheduleSurrogates(t *testing.T) {
|
|||||||
for i := 0; i < numTeams; i++ {
|
for i := 0; i < numTeams; i++ {
|
||||||
teams[i].Id = i + 101
|
teams[i].Id = i + 101
|
||||||
}
|
}
|
||||||
scheduleBlocks := []ScheduleBlock{{time.Unix(0, 0).UTC(), 64, 60}}
|
scheduleBlocks := []model.ScheduleBlock{{0, "", time.Unix(0, 0).UTC(), 64, 60}}
|
||||||
matches, _ := BuildRandomSchedule(teams, scheduleBlocks, "test")
|
matches, _ := BuildRandomSchedule(teams, scheduleBlocks, "test")
|
||||||
for i, match := range matches {
|
for i, match := range matches {
|
||||||
if i == 13 || i == 14 {
|
if i == 13 || i == 14 {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/Team254/cheesy-arena/game"
|
"github.com/Team254/cheesy-arena/game"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -65,7 +64,6 @@ func TestAllianceStationDisplayWebsocket(t *testing.T) {
|
|||||||
web.arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec) * time.Second)
|
web.arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec) * time.Second)
|
||||||
web.arena.Update()
|
web.arena.Update()
|
||||||
messages := readWebsocketMultiple(t, ws, 2)
|
messages := readWebsocketMultiple(t, ws, 2)
|
||||||
fmt.Println(messages)
|
|
||||||
_, ok := messages["status"]
|
_, ok := messages["status"]
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
_, ok = messages["matchTime"]
|
_, ok = messages["matchTime"]
|
||||||
|
|||||||
@@ -15,10 +15,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Global vars to hold schedules that are in the process of being generated.
|
// Global vars to hold schedules that are in the process of being generated.
|
||||||
var cachedMatchType string
|
var cachedMatches = make(map[string][]model.Match)
|
||||||
var cachedScheduleBlocks []tournament.ScheduleBlock
|
var cachedTeamFirstMatches = make(map[string]map[int]string)
|
||||||
var cachedMatches []model.Match
|
|
||||||
var cachedTeamFirstMatches map[int]string
|
|
||||||
|
|
||||||
// Shows the schedule editing page.
|
// Shows the schedule editing page.
|
||||||
func (web *Web) scheduleGetHandler(w http.ResponseWriter, r *http.Request) {
|
func (web *Web) scheduleGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -26,22 +24,41 @@ func (web *Web) scheduleGetHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cachedScheduleBlocks) == 0 {
|
matchType := getMatchType(r)
|
||||||
cachedMatchType = "practice"
|
if matchType == "" {
|
||||||
|
http.Redirect(w, r, "/setup/schedule?matchType=practice", 302)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if matchType != "practice" && matchType != "qualification" {
|
||||||
|
handleWebErr(w, fmt.Errorf("Invalid match type '%s'.", matchType))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
web.renderSchedule(w, r, "")
|
web.renderSchedule(w, r, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates the schedule and presents it for review without saving it to the database.
|
// Generates the schedule, presents it for review without saving it, and saves the schedule blocks to the database.
|
||||||
func (web *Web) scheduleGeneratePostHandler(w http.ResponseWriter, r *http.Request) {
|
func (web *Web) scheduleGeneratePostHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if !web.userIsAdmin(w, r) {
|
if !web.userIsAdmin(w, r) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
r.ParseForm()
|
matchType := getMatchType(r)
|
||||||
cachedMatchType = r.PostFormValue("matchType")
|
|
||||||
scheduleBlocks, err := getScheduleBlocks(r)
|
scheduleBlocks, err := getScheduleBlocks(r)
|
||||||
cachedScheduleBlocks = scheduleBlocks // Show the same blocks even if there is an error.
|
// Save blocks even if there is an error, so that any good ones are not discarded.
|
||||||
|
deleteBlocksErr := web.arena.Database.DeleteScheduleBlocksByMatchType(matchType)
|
||||||
|
if deleteBlocksErr != nil {
|
||||||
|
handleWebErr(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, block := range scheduleBlocks {
|
||||||
|
block.MatchType = matchType
|
||||||
|
createBlockErr := web.arena.Database.CreateScheduleBlock(&block)
|
||||||
|
if createBlockErr != nil {
|
||||||
|
handleWebErr(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
web.renderSchedule(w, r, "Incomplete or invalid schedule block parameters specified.")
|
web.renderSchedule(w, r, "Incomplete or invalid schedule block parameters specified.")
|
||||||
return
|
return
|
||||||
@@ -68,7 +85,7 @@ func (web *Web) scheduleGeneratePostHandler(w http.ResponseWriter, r *http.Reque
|
|||||||
web.renderSchedule(w, r, fmt.Sprintf("Error generating schedule: %s.", err.Error()))
|
web.renderSchedule(w, r, fmt.Sprintf("Error generating schedule: %s.", err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cachedMatches = matches
|
cachedMatches[matchType] = matches
|
||||||
|
|
||||||
// Determine each team's first match.
|
// Determine each team's first match.
|
||||||
teamFirstMatches := make(map[int]string)
|
teamFirstMatches := make(map[int]string)
|
||||||
@@ -86,9 +103,9 @@ func (web *Web) scheduleGeneratePostHandler(w http.ResponseWriter, r *http.Reque
|
|||||||
checkTeam(match.Blue2)
|
checkTeam(match.Blue2)
|
||||||
checkTeam(match.Blue3)
|
checkTeam(match.Blue3)
|
||||||
}
|
}
|
||||||
cachedTeamFirstMatches = teamFirstMatches
|
cachedTeamFirstMatches[matchType] = teamFirstMatches
|
||||||
|
|
||||||
http.Redirect(w, r, "/setup/schedule", 303)
|
http.Redirect(w, r, "/setup/schedule?matchType="+matchType, 303)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publishes the schedule in the database to TBA
|
// Publishes the schedule in the database to TBA
|
||||||
@@ -110,7 +127,7 @@ func (web *Web) scheduleRepublishPostHandler(w http.ResponseWriter, r *http.Requ
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, "/setup/schedule", 303)
|
http.Redirect(w, r, "/setup/schedule"+getMatchType(r), 303)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saves the generated schedule to the database.
|
// Saves the generated schedule to the database.
|
||||||
@@ -119,18 +136,19 @@ func (web *Web) scheduleSavePostHandler(w http.ResponseWriter, r *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
existingMatches, err := web.arena.Database.GetMatchesByType(cachedMatchType)
|
matchType := getMatchType(r)
|
||||||
|
existingMatches, err := web.arena.Database.GetMatchesByType(matchType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleWebErr(w, err)
|
handleWebErr(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(existingMatches) > 0 {
|
if len(existingMatches) > 0 {
|
||||||
web.renderSchedule(w, r, fmt.Sprintf("Can't save schedule because a schedule of %d %s matches already "+
|
web.renderSchedule(w, r, fmt.Sprintf("Can't save schedule because a schedule of %d %s matches already "+
|
||||||
"exists. Clear it first on the Settings page.", len(existingMatches), cachedMatchType))
|
"exists. Clear it first on the Settings page.", len(existingMatches), matchType))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, match := range cachedMatches {
|
for _, match := range cachedMatches[matchType] {
|
||||||
err = web.arena.Database.CreateMatch(&match)
|
err = web.arena.Database.CreateMatch(&match)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleWebErr(w, err)
|
handleWebErr(w, err)
|
||||||
@@ -145,7 +163,7 @@ func (web *Web) scheduleSavePostHandler(w http.ResponseWriter, r *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if web.arena.EventSettings.TbaPublishingEnabled && cachedMatchType != "practice" {
|
if web.arena.EventSettings.TbaPublishingEnabled && matchType != "practice" {
|
||||||
// Publish schedule to The Blue Alliance.
|
// Publish schedule to The Blue Alliance.
|
||||||
err = web.arena.TbaClient.DeletePublishedMatches()
|
err = web.arena.TbaClient.DeletePublishedMatches()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -159,10 +177,17 @@ func (web *Web) scheduleSavePostHandler(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, "/setup/schedule", 303)
|
http.Redirect(w, r, "/setup/schedule?matchType="+matchType, 303)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (web *Web) renderSchedule(w http.ResponseWriter, r *http.Request, errorMessage string) {
|
func (web *Web) renderSchedule(w http.ResponseWriter, r *http.Request, errorMessage string) {
|
||||||
|
matchType := getMatchType(r)
|
||||||
|
scheduleBlocks, err := web.arena.Database.GetScheduleBlocksByMatchType(matchType)
|
||||||
|
if err != nil {
|
||||||
|
handleWebErr(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
teams, err := web.arena.Database.GetAllTeams()
|
teams, err := web.arena.Database.GetAllTeams()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleWebErr(w, err)
|
handleWebErr(w, err)
|
||||||
@@ -176,13 +201,13 @@ func (web *Web) renderSchedule(w http.ResponseWriter, r *http.Request, errorMess
|
|||||||
data := struct {
|
data := struct {
|
||||||
*model.EventSettings
|
*model.EventSettings
|
||||||
MatchType string
|
MatchType string
|
||||||
ScheduleBlocks []tournament.ScheduleBlock
|
ScheduleBlocks []model.ScheduleBlock
|
||||||
NumTeams int
|
NumTeams int
|
||||||
Matches []model.Match
|
Matches []model.Match
|
||||||
TeamFirstMatches map[int]string
|
TeamFirstMatches map[int]string
|
||||||
ErrorMessage string
|
ErrorMessage string
|
||||||
}{web.arena.EventSettings, cachedMatchType, cachedScheduleBlocks, len(teams), cachedMatches, cachedTeamFirstMatches,
|
}{web.arena.EventSettings, matchType, scheduleBlocks, len(teams), cachedMatches[matchType],
|
||||||
errorMessage}
|
cachedTeamFirstMatches[matchType], errorMessage}
|
||||||
err = template.ExecuteTemplate(w, "base", data)
|
err = template.ExecuteTemplate(w, "base", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleWebErr(w, err)
|
handleWebErr(w, err)
|
||||||
@@ -191,13 +216,13 @@ func (web *Web) renderSchedule(w http.ResponseWriter, r *http.Request, errorMess
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Converts the post form variables into a slice of schedule blocks.
|
// Converts the post form variables into a slice of schedule blocks.
|
||||||
func getScheduleBlocks(r *http.Request) ([]tournament.ScheduleBlock, error) {
|
func getScheduleBlocks(r *http.Request) ([]model.ScheduleBlock, error) {
|
||||||
numScheduleBlocks, err := strconv.Atoi(r.PostFormValue("numScheduleBlocks"))
|
numScheduleBlocks, err := strconv.Atoi(r.PostFormValue("numScheduleBlocks"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []tournament.ScheduleBlock{}, err
|
return []model.ScheduleBlock{}, err
|
||||||
}
|
}
|
||||||
var returnErr error
|
var returnErr error
|
||||||
scheduleBlocks := make([]tournament.ScheduleBlock, numScheduleBlocks)
|
scheduleBlocks := make([]model.ScheduleBlock, numScheduleBlocks)
|
||||||
location, _ := time.LoadLocation("Local")
|
location, _ := time.LoadLocation("Local")
|
||||||
for i := 0; i < numScheduleBlocks; i++ {
|
for i := 0; i < numScheduleBlocks; i++ {
|
||||||
scheduleBlocks[i].StartTime, err = time.ParseInLocation("2006-01-02 03:04:05 PM",
|
scheduleBlocks[i].StartTime, err = time.ParseInLocation("2006-01-02 03:04:05 PM",
|
||||||
@@ -216,3 +241,11 @@ func getScheduleBlocks(r *http.Request) ([]tournament.ScheduleBlock, error) {
|
|||||||
}
|
}
|
||||||
return scheduleBlocks, returnErr
|
return scheduleBlocks, returnErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMatchType(r *http.Request) string {
|
||||||
|
if matchType, ok := r.URL.Query()["matchType"]; ok {
|
||||||
|
return matchType[0]
|
||||||
|
}
|
||||||
|
r.ParseForm()
|
||||||
|
return r.PostFormValue("matchType")
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func TestSetupSchedule(t *testing.T) {
|
|||||||
web.arena.Database.CreateMatch(&model.Match{Type: "practice", DisplayName: "1"})
|
web.arena.Database.CreateMatch(&model.Match{Type: "practice", DisplayName: "1"})
|
||||||
|
|
||||||
// Check the default setting values.
|
// Check the default setting values.
|
||||||
recorder := web.getHttpResponse("/setup/schedule")
|
recorder := web.getHttpResponse("/setup/schedule?matchType=practice")
|
||||||
assert.Equal(t, 200, recorder.Code)
|
assert.Equal(t, 200, recorder.Code)
|
||||||
assert.Contains(t, recorder.Body.String(), "addBlock();")
|
assert.Contains(t, recorder.Body.String(), "addBlock();")
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ func TestSetupSchedule(t *testing.T) {
|
|||||||
"numMatches2=40&matchSpacingSec2=360&matchType=qualification"
|
"numMatches2=40&matchSpacingSec2=360&matchType=qualification"
|
||||||
recorder = web.postHttpResponse("/setup/schedule/generate", postData)
|
recorder = web.postHttpResponse("/setup/schedule/generate", postData)
|
||||||
assert.Equal(t, 303, recorder.Code)
|
assert.Equal(t, 303, recorder.Code)
|
||||||
recorder = web.getHttpResponse("/setup/schedule")
|
recorder = web.getHttpResponse("/setup/schedule?matchType=qualification")
|
||||||
assert.Contains(t, recorder.Body.String(), "2014-01-01 09:48:00") // Last match of first block.
|
assert.Contains(t, recorder.Body.String(), "2014-01-01 09:48:00") // Last match of first block.
|
||||||
assert.Contains(t, recorder.Body.String(), "2014-01-02 11:48:00") // Last match of second block.
|
assert.Contains(t, recorder.Body.String(), "2014-01-02 11:48:00") // Last match of second block.
|
||||||
assert.Contains(t, recorder.Body.String(), "2014-01-03 16:54:00") // Last match of third block.
|
assert.Contains(t, recorder.Body.String(), "2014-01-03 16:54:00") // Last match of third block.
|
||||||
@@ -37,7 +37,7 @@ func TestSetupSchedule(t *testing.T) {
|
|||||||
// Save schedule and check that it is published to TBA.
|
// Save schedule and check that it is published to TBA.
|
||||||
web.arena.TbaClient.BaseUrl = "fakeUrl"
|
web.arena.TbaClient.BaseUrl = "fakeUrl"
|
||||||
web.arena.EventSettings.TbaPublishingEnabled = true
|
web.arena.EventSettings.TbaPublishingEnabled = true
|
||||||
recorder = web.postHttpResponse("/setup/schedule/save", "")
|
recorder = web.postHttpResponse("/setup/schedule/save?matchType=qualification", "")
|
||||||
matches, err := web.arena.Database.GetMatchesByType("qualification")
|
matches, err := web.arena.Database.GetMatchesByType("qualification")
|
||||||
assert.Equal(t, 500, recorder.Code)
|
assert.Equal(t, 500, recorder.Code)
|
||||||
assert.Contains(t, recorder.Body.String(), "Failed to delete published matches")
|
assert.Contains(t, recorder.Body.String(), "Failed to delete published matches")
|
||||||
|
|||||||
Reference in New Issue
Block a user