Files
cheesy-arena-lite/web/setup_schedule.go

252 lines
7.1 KiB
Go
Raw Normal View History

2014-06-08 21:47:31 -07:00
// Copyright 2014 Team 254. All Rights Reserved.
// Author: pat@patfairbank.com (Patrick Fairbank)
//
// Web routes for generating practice and qualification schedules.
package web
2014-06-08 21:47:31 -07:00
import (
"fmt"
2021-05-16 11:00:29 -07:00
"github.com/Team254/cheesy-arena-lite/model"
"github.com/Team254/cheesy-arena-lite/tournament"
2014-06-08 21:47:31 -07:00
"net/http"
"strconv"
"time"
)
// Global vars to hold schedules that are in the process of being generated.
2018-08-24 20:51:37 -07:00
var cachedMatches = make(map[string][]model.Match)
var cachedTeamFirstMatches = make(map[string]map[int]string)
2014-06-08 21:47:31 -07:00
// Shows the schedule editing page.
func (web *Web) scheduleGetHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsAdmin(w, r) {
2015-08-22 23:33:38 -07:00
return
}
2018-08-24 20:51:37 -07:00
matchType := getMatchType(r)
if matchType == "" {
http.Redirect(w, r, "/setup/schedule?matchType=practice", 302)
2022-08-23 17:52:41 -07:00
return
2014-06-08 21:47:31 -07:00
}
2018-08-24 20:51:37 -07:00
if matchType != "practice" && matchType != "qualification" {
handleWebErr(w, fmt.Errorf("Invalid match type '%s'.", matchType))
return
}
web.renderSchedule(w, r, "")
2014-06-08 21:47:31 -07:00
}
2018-08-24 20:51:37 -07:00
// 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) {
if !web.userIsAdmin(w, r) {
2015-08-22 23:33:38 -07:00
return
}
2018-08-24 20:51:37 -07:00
matchType := getMatchType(r)
2014-06-08 21:47:31 -07:00
scheduleBlocks, err := getScheduleBlocks(r)
2018-08-24 20:51:37 -07:00
// 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
}
}
2014-06-08 21:47:31 -07:00
if err != nil {
web.renderSchedule(w, r, "Incomplete or invalid schedule block parameters specified.")
2014-06-08 21:47:31 -07:00
return
}
// Build the schedule.
teams, err := web.arena.Database.GetAllTeams()
2014-06-08 21:47:31 -07:00
if err != nil {
handleWebErr(w, err)
return
}
if len(teams) == 0 {
web.renderSchedule(w, r, "No team list is configured. Set up the list of teams at the event before "+
2014-06-10 21:45:40 -07:00
"generating the schedule.")
2014-06-08 21:47:31 -07:00
return
}
if len(teams) < 6 {
web.renderSchedule(w, r, fmt.Sprintf("There are only %d teams. There must be at least 6 teams to generate "+
2014-06-10 21:45:40 -07:00
"a schedule.", len(teams)))
2014-06-08 21:47:31 -07:00
return
}
matches, err := tournament.BuildRandomSchedule(teams, scheduleBlocks, r.PostFormValue("matchType"))
2014-06-08 21:47:31 -07:00
if err != nil {
web.renderSchedule(w, r, fmt.Sprintf("Error generating schedule: %s.", err.Error()))
2014-06-08 21:47:31 -07:00
return
}
2018-08-24 20:51:37 -07:00
cachedMatches[matchType] = matches
2014-06-08 21:47:31 -07:00
// Determine each team's first match.
teamFirstMatches := make(map[int]string)
for _, match := range matches {
checkTeam := func(team int) {
_, ok := teamFirstMatches[team]
if !ok {
teamFirstMatches[team] = match.DisplayName
}
}
checkTeam(match.Red1)
checkTeam(match.Red2)
checkTeam(match.Red3)
checkTeam(match.Blue1)
checkTeam(match.Blue2)
checkTeam(match.Blue3)
}
2018-08-24 20:51:37 -07:00
cachedTeamFirstMatches[matchType] = teamFirstMatches
2018-08-24 20:51:37 -07:00
http.Redirect(w, r, "/setup/schedule?matchType="+matchType, 303)
2014-06-08 21:47:31 -07:00
}
// Publishes the schedule in the database to TBA
func (web *Web) scheduleRepublishPostHandler(w http.ResponseWriter, r *http.Request) {
if web.arena.EventSettings.TbaPublishingEnabled {
// Publish schedule to The Blue Alliance.
err := web.arena.TbaClient.DeletePublishedMatches()
if err != nil {
http.Error(w, "Failed to delete published matches: "+err.Error(), 500)
return
}
err = web.arena.TbaClient.PublishMatches(web.arena.Database)
if err != nil {
http.Error(w, "Failed to publish matches: "+err.Error(), 500)
return
}
} else {
http.Error(w, "TBA publishing is not enabled", 500)
return
}
http.Redirect(w, r, "/setup/schedule?matchType="+getMatchType(r), 303)
}
2014-06-08 21:47:31 -07:00
// Saves the generated schedule to the database.
func (web *Web) scheduleSavePostHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsAdmin(w, r) {
2015-08-22 23:33:38 -07:00
return
}
2018-08-24 20:51:37 -07:00
matchType := getMatchType(r)
existingMatches, err := web.arena.Database.GetMatchesByType(matchType)
2014-06-08 21:47:31 -07:00
if err != nil {
handleWebErr(w, err)
return
}
if len(existingMatches) > 0 {
web.renderSchedule(w, r, fmt.Sprintf("Can't save schedule because a schedule of %d %s matches already "+
2018-08-24 20:51:37 -07:00
"exists. Clear it first on the Settings page.", len(existingMatches), matchType))
2014-06-08 21:47:31 -07:00
return
}
2018-08-24 20:51:37 -07:00
for _, match := range cachedMatches[matchType] {
err = web.arena.Database.CreateMatch(&match)
2014-06-08 21:47:31 -07:00
if err != nil {
handleWebErr(w, err)
return
}
}
2014-07-30 22:55:14 -07:00
2014-09-05 22:17:12 -07:00
// Back up the database.
err = web.arena.Database.Backup(web.arena.EventSettings.Name, "post_scheduling")
2014-09-05 22:17:12 -07:00
if err != nil {
handleWebErr(w, err)
return
}
2018-08-24 20:51:37 -07:00
if web.arena.EventSettings.TbaPublishingEnabled && matchType != "practice" {
2014-07-30 22:55:14 -07:00
// Publish schedule to The Blue Alliance.
err = web.arena.TbaClient.DeletePublishedMatches()
if err != nil {
http.Error(w, "Failed to delete published matches: "+err.Error(), 500)
return
}
err = web.arena.TbaClient.PublishMatches(web.arena.Database)
2014-07-30 22:55:14 -07:00
if err != nil {
2014-09-05 22:17:12 -07:00
http.Error(w, "Failed to publish matches: "+err.Error(), 500)
2014-07-30 22:55:14 -07:00
return
}
}
2018-08-24 20:51:37 -07:00
http.Redirect(w, r, "/setup/schedule?matchType="+matchType, 303)
2014-06-08 21:47:31 -07:00
}
func (web *Web) renderSchedule(w http.ResponseWriter, r *http.Request, errorMessage string) {
2018-08-24 20:51:37 -07:00
matchType := getMatchType(r)
scheduleBlocks, err := web.arena.Database.GetScheduleBlocksByMatchType(matchType)
if err != nil {
handleWebErr(w, err)
return
}
teams, err := web.arena.Database.GetAllTeams()
2014-06-08 21:47:31 -07:00
if err != nil {
handleWebErr(w, err)
return
}
template, err := web.parseFiles("templates/setup_schedule.html", "templates/base.html")
2014-06-08 21:47:31 -07:00
if err != nil {
handleWebErr(w, err)
return
}
data := struct {
*model.EventSettings
2014-06-08 21:47:31 -07:00
MatchType string
2018-08-24 20:51:37 -07:00
ScheduleBlocks []model.ScheduleBlock
2014-06-08 21:47:31 -07:00
NumTeams int
Matches []model.Match
2014-06-08 21:47:31 -07:00
TeamFirstMatches map[int]string
ErrorMessage string
2018-08-24 20:51:37 -07:00
}{web.arena.EventSettings, matchType, scheduleBlocks, len(teams), cachedMatches[matchType],
cachedTeamFirstMatches[matchType], errorMessage}
2014-06-08 21:47:31 -07:00
err = template.ExecuteTemplate(w, "base", data)
if err != nil {
handleWebErr(w, err)
return
}
}
// Converts the post form variables into a slice of schedule blocks.
2018-08-24 20:51:37 -07:00
func getScheduleBlocks(r *http.Request) ([]model.ScheduleBlock, error) {
2014-06-08 21:47:31 -07:00
numScheduleBlocks, err := strconv.Atoi(r.PostFormValue("numScheduleBlocks"))
if err != nil {
2018-08-24 20:51:37 -07:00
return []model.ScheduleBlock{}, err
2014-06-08 21:47:31 -07:00
}
var returnErr error
2018-08-24 20:51:37 -07:00
scheduleBlocks := make([]model.ScheduleBlock, numScheduleBlocks)
2014-06-08 21:47:31 -07:00
location, _ := time.LoadLocation("Local")
for i := 0; i < numScheduleBlocks; i++ {
scheduleBlocks[i].StartTime, err = time.ParseInLocation("2006-01-02 03:04:05 PM",
r.PostFormValue(fmt.Sprintf("startTime%d", i)), location)
if err != nil {
returnErr = err
2014-06-08 21:47:31 -07:00
}
scheduleBlocks[i].NumMatches, err = strconv.Atoi(r.PostFormValue(fmt.Sprintf("numMatches%d", i)))
if err != nil {
returnErr = err
2014-06-08 21:47:31 -07:00
}
scheduleBlocks[i].MatchSpacingSec, err = strconv.Atoi(r.PostFormValue(fmt.Sprintf("matchSpacingSec%d", i)))
if err != nil {
returnErr = err
2014-06-08 21:47:31 -07:00
}
}
return scheduleBlocks, returnErr
2014-06-08 21:47:31 -07:00
}
2018-08-24 20:51:37 -07:00
func getMatchType(r *http.Request) string {
if matchType, ok := r.URL.Query()["matchType"]; ok {
return matchType[0]
}
return r.PostFormValue("matchType")
}