mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-10 06:06:47 -04:00
Merge branch 'master' into changes_for_2015_game
This commit is contained in:
1
TODO.md
1
TODO.md
@@ -35,6 +35,7 @@ Cheesy Arena To-Do List
|
||||
* Automatic download of recent accomplishments (needs better TBA API)
|
||||
|
||||
###Development tasks
|
||||
* Change to use the new FMS API for team data
|
||||
* Generate more schedules and find an automated way to evaluate them
|
||||
* Clean up sponsor carousel JavaScript and make it load new slides asynchronously without needing a reload of the audience display page
|
||||
* Refactor websockets to reduce code repetition between displays with similar functions
|
||||
|
||||
3
db/migrations/20150401132914_AddTBAColumns.sql
Normal file
3
db/migrations/20150401132914_AddTBAColumns.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE event_settings ADD COLUMN tbadownloadenabled BOOLEAN;
|
||||
ALTER TABLE event_settings ADD COLUMN tbaawardsdownloadenabled BOOLEAN;
|
||||
@@ -6,26 +6,28 @@
|
||||
package main
|
||||
|
||||
type EventSettings struct {
|
||||
Id int
|
||||
Name string
|
||||
Code string
|
||||
DisplayBackgroundColor string
|
||||
NumElimAlliances int
|
||||
SelectionRound2Order string
|
||||
SelectionRound3Order string
|
||||
TeamInfoDownloadEnabled bool
|
||||
RedGoalLightsAddress string
|
||||
BlueGoalLightsAddress string
|
||||
TbaPublishingEnabled bool
|
||||
TbaEventCode string
|
||||
TbaSecretId string
|
||||
TbaSecret string
|
||||
NetworkSecurityEnabled bool
|
||||
ApAddress string
|
||||
ApUsername string
|
||||
ApPassword string
|
||||
SwitchAddress string
|
||||
SwitchPassword string
|
||||
Id int
|
||||
Name string
|
||||
Code string
|
||||
DisplayBackgroundColor string
|
||||
NumElimAlliances int
|
||||
SelectionRound2Order string
|
||||
SelectionRound3Order string
|
||||
TBADownloadEnabled bool
|
||||
TBAAwardsDownloadEnabled bool
|
||||
AllianceDisplayHotGoals bool
|
||||
RedGoalLightsAddress string
|
||||
BlueGoalLightsAddress string
|
||||
TbaPublishingEnabled bool
|
||||
TbaEventCode string
|
||||
TbaSecretId string
|
||||
TbaSecret string
|
||||
NetworkSecurityEnabled bool
|
||||
ApAddress string
|
||||
ApUsername string
|
||||
ApPassword string
|
||||
SwitchAddress string
|
||||
SwitchPassword string
|
||||
BandwidthMonitoringEnabled bool
|
||||
}
|
||||
|
||||
@@ -42,7 +44,8 @@ func (database *Database) GetEventSettings() (*EventSettings, error) {
|
||||
eventSettings.NumElimAlliances = 8
|
||||
eventSettings.SelectionRound2Order = "L"
|
||||
eventSettings.SelectionRound3Order = ""
|
||||
eventSettings.TeamInfoDownloadEnabled = true
|
||||
eventSettings.TBADownloadEnabled = true
|
||||
eventSettings.TBAAwardsDownloadEnabled = true
|
||||
err = database.eventSettingsMap.Insert(eventSettings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestEventSettingsReadWrite(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, EventSettings{Id: 0, Name: "Untitled Event", Code: "UE", DisplayBackgroundColor: "#00ff00",
|
||||
NumElimAlliances: 8, SelectionRound2Order: "L", SelectionRound3Order: "",
|
||||
TeamInfoDownloadEnabled: true}, *eventSettings)
|
||||
TBADownloadEnabled: true, TBAAwardsDownloadEnabled: true}, *eventSettings)
|
||||
|
||||
eventSettings.Name = "Chezy Champs"
|
||||
eventSettings.Code = "cc"
|
||||
|
||||
@@ -42,7 +42,8 @@ func SettingsPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
eventSettings.NumElimAlliances = numAlliances
|
||||
eventSettings.SelectionRound2Order = r.PostFormValue("selectionRound2Order")
|
||||
eventSettings.SelectionRound3Order = r.PostFormValue("selectionRound3Order")
|
||||
eventSettings.TeamInfoDownloadEnabled = r.PostFormValue("teamInfoDownloadEnabled") == "on"
|
||||
eventSettings.TBADownloadEnabled = r.PostFormValue("TBADownloadEnabled") == "on"
|
||||
eventSettings.TBAAwardsDownloadEnabled = r.PostFormValue("TBAAwardsDownloadEnabled") == "on"
|
||||
eventSettings.RedGoalLightsAddress = r.PostFormValue("redGoalLightsAddress")
|
||||
eventSettings.BlueGoalLightsAddress = r.PostFormValue("blueGoalLightsAddress")
|
||||
eventSettings.TbaPublishingEnabled = r.PostFormValue("tbaPublishingEnabled") == "on"
|
||||
|
||||
@@ -6,24 +6,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/dchest/uniuri"
|
||||
"github.com/gorilla/mux"
|
||||
"html"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const wpaKeyLength = 8
|
||||
|
||||
var officialTeamInfoUrl = "https://my.usfirst.org/frc/scoring/index.lasso?page=teamlist"
|
||||
var officialTeamInfo map[int][]string
|
||||
var officialTeamInfoUrl = "http://www.thebluealliance.com/api/v2/team/"
|
||||
|
||||
// Shows the team list.
|
||||
func TeamsGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -235,52 +231,42 @@ func canModifyTeamList() bool {
|
||||
|
||||
// Returns the data for the given team number.
|
||||
func getOfficialTeamInfo(teamId int) (*Team, error) {
|
||||
if officialTeamInfo == nil && eventSettings.TeamInfoDownloadEnabled {
|
||||
// Download all team info from the FIRST website if it is not cached.
|
||||
resp, err := http.Get(officialTeamInfoUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
re := regexp.MustCompile("(?s).*<PRE>(.*)</PRE>.*")
|
||||
teamsCsv := re.FindStringSubmatch(string(body))[1]
|
||||
|
||||
// Parse the tab-separated data.
|
||||
reader := csv.NewReader(strings.NewReader(teamsCsv))
|
||||
reader.Comma = '\t'
|
||||
reader.FieldsPerRecord = -1
|
||||
officialTeamInfo = make(map[int][]string)
|
||||
reader.Read() // Ignore header line.
|
||||
for {
|
||||
fields, err := reader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
teamNumber, err := strconv.Atoi(fields[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
officialTeamInfo[teamNumber] = fields
|
||||
}
|
||||
}
|
||||
|
||||
teamData, ok := officialTeamInfo[teamId]
|
||||
// Create the team variable that stores the result
|
||||
var team Team
|
||||
if ok {
|
||||
rookieYear, _ := strconv.Atoi(teamData[8])
|
||||
team = Team{Id: teamId, Name: html.UnescapeString(teamData[2]), Nickname: html.UnescapeString(teamData[7]),
|
||||
City: html.UnescapeString(teamData[4]), StateProv: html.UnescapeString(teamData[5]),
|
||||
Country: html.UnescapeString(teamData[6]), RookieYear: rookieYear,
|
||||
RobotName: html.UnescapeString(teamData[9])}
|
||||
|
||||
// If team info download is enabled, download the current teams data (caching isn't easy with the new paging system in the api)
|
||||
if eventSettings.TBADownloadEnabled {
|
||||
var tbaTeam *TbaTeam = getTeamFromTba(teamId)
|
||||
|
||||
// Check if the result is valid. If a team is not found, just return a basic team
|
||||
if tbaTeam == nil {
|
||||
team = Team{Id: teamId}
|
||||
return &team, nil
|
||||
}
|
||||
|
||||
var recentAwards []TbaAward
|
||||
if eventSettings.TBAAwardsDownloadEnabled {
|
||||
recentAwards = getTeamAwardsFromTba(teamId)
|
||||
}
|
||||
|
||||
var accomplishmentsBuffer bytes.Buffer
|
||||
|
||||
// Generate accomplishments string
|
||||
for _, award := range recentAwards {
|
||||
if time.Now().Year()-award.Year <= 2 {
|
||||
accomplishmentsBuffer.WriteString(fmt.Sprint(award.Year, " - ", award.Name, "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
// Use those variables to make a team object
|
||||
team = Team{Id: teamId, Name: tbaTeam.Name, Nickname: tbaTeam.Nickname,
|
||||
City: tbaTeam.Locality, StateProv: tbaTeam.Reigon,
|
||||
Country: tbaTeam.Country, RookieYear: tbaTeam.RookieYear, Accomplishments: accomplishmentsBuffer.String()}
|
||||
} else {
|
||||
// If no team data exists, just fill in the team number.
|
||||
// If team grab is disabled, just use the team number
|
||||
team = Team{Id: teamId}
|
||||
}
|
||||
|
||||
// Return the team object
|
||||
return &team, nil
|
||||
}
|
||||
|
||||
75
tba.go
75
tba.go
@@ -17,6 +17,8 @@ import (
|
||||
|
||||
var tbaBaseUrl = "http://www.thebluealliance.com"
|
||||
|
||||
// MODELS
|
||||
|
||||
type TbaMatch struct {
|
||||
CompLevel string `json:"comp_level"`
|
||||
MatchNumber int `json:"match_number"`
|
||||
@@ -48,6 +50,63 @@ type TbaRanking struct {
|
||||
Played int `json:"played"`
|
||||
}
|
||||
|
||||
type TbaTeam struct {
|
||||
Website string `json:"website"`
|
||||
Name string `json:"name"`
|
||||
Locality string `json:"locality"`
|
||||
RookieYear int `json:"rookie_year"`
|
||||
Reigon string `json:"region"`
|
||||
TeamNumber int `json:"team_number"`
|
||||
Location string `json:"location"`
|
||||
Key string `json:"key"`
|
||||
Country string `json:"country_name"`
|
||||
Nickname string `json:"nickname"`
|
||||
}
|
||||
|
||||
type TbaAward struct {
|
||||
Name string `json:"name"`
|
||||
EventKey string `json:"event_key"`
|
||||
Year int `json:"year"`
|
||||
AwardType int `json:"award_type"`
|
||||
}
|
||||
|
||||
// DATA RETRIEVAL
|
||||
func getTeamFromTba(teamNumber int) *TbaTeam {
|
||||
url := fmt.Sprint("/api/v2/team/", string(getTbaTeam(teamNumber)))
|
||||
resp, _ := getTbaRequest(url)
|
||||
|
||||
// Get the response and handle errors
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var teamData TbaTeam
|
||||
json.Unmarshal(body, &teamData)
|
||||
|
||||
return &teamData
|
||||
}
|
||||
|
||||
func getTeamAwardsFromTba(teamNumber int) []TbaAward {
|
||||
url := fmt.Sprint("/api/v2/team/", string(getTbaTeam(teamNumber)), "/history/awards")
|
||||
resp, _ := getTbaRequest(url)
|
||||
|
||||
// Get the response and handle errors
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var awardData []TbaAward
|
||||
json.Unmarshal(body, &awardData)
|
||||
|
||||
return awardData
|
||||
}
|
||||
|
||||
// PUBLISHING
|
||||
|
||||
// Uploads the event team list to The Blue Alliance.
|
||||
func PublishTeams() error {
|
||||
teams, err := db.GetAllTeams()
|
||||
@@ -153,7 +212,7 @@ func PublishRankings() error {
|
||||
}
|
||||
|
||||
// Build a JSON object of TBA-format rankings.
|
||||
breakdowns := []string{"QA", "Coopertition", "Auto", "Container", "Tote", "Litter"}
|
||||
breakdowns := []string{"QS", "Assist", "Auto", "T&C", "G&F", "wins", "losses", "ties"}
|
||||
tbaRankings := make([]TbaRanking, len(rankings))
|
||||
for i, ranking := range rankings {
|
||||
tbaRankings[i] = TbaRanking{getTbaTeam(ranking.TeamId), ranking.Rank, ranking.QualificationAverage,
|
||||
@@ -213,6 +272,8 @@ func getTbaTeam(team int) string {
|
||||
return fmt.Sprintf("frc%d", team)
|
||||
}
|
||||
|
||||
// HELPERS
|
||||
|
||||
// Signs the request and sends it to the TBA API.
|
||||
func postTbaRequest(resource string, body []byte) (*http.Response, error) {
|
||||
path := fmt.Sprintf("/api/trusted/v1/event/%s/%s/update", eventSettings.TbaEventCode, resource)
|
||||
@@ -227,3 +288,15 @@ func postTbaRequest(resource string, body []byte) (*http.Response, error) {
|
||||
request.Header.Add("X-TBA-Auth-Sig", signature)
|
||||
return client.Do(request)
|
||||
}
|
||||
|
||||
// Sends a GET request to the TBA API
|
||||
func getTbaRequest(path string) (*http.Response, error) {
|
||||
// Make an HTTP GET request with the TBA auth headers
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("GET", fmt.Sprint(tbaBaseUrl, path), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("X-TBA-App-Id", "cheesy-arena:cheesy-fms:v0.1")
|
||||
return client.Do(req)
|
||||
}
|
||||
|
||||
@@ -17,14 +17,22 @@
|
||||
<link href="/static/css/lib/bootstrap-colorpicker.min.css" rel="stylesheet">
|
||||
<link href="/static/css/lib/bootstrap-datetimepicker.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="navbar navbar-default navbar-static-top" role="navigation">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="/">Cheesy Arena</a>
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse"
|
||||
data-target="#navbar-collapse-menu">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<div class="navbar-collapse collapse" id="navbar-collapse-menu">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Setup</a>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
{{template "ftaTeam" dict "color" "B" "position" 3 "data" .}}
|
||||
</div>
|
||||
<div class="col-lg-6 well well-darkred">
|
||||
<div class="row form-group">
|
||||
<div class="row form-group hidden-xs">
|
||||
<div class="col-xs-3">Red Teams</div>
|
||||
<div class="col-xs-1" data-toggle="tooltip" title="Driver Station (Tx/Rx MB/s)">DS</div>
|
||||
<div class="col-xs-1" data-toggle="tooltip" title="Robot Status/Time Since Last Link">R</div>
|
||||
|
||||
@@ -91,10 +91,19 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Automatic Team Info Download</legend>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-7 control-label">Enable Team Info Download From usfirst.org</label>
|
||||
<label class="col-lg-9 control-label">Enable Automatic Team Info Download (From TBA)</label>
|
||||
<div class="col-lg-1 checkbox">
|
||||
<input type="checkbox" name="teamInfoDownloadEnabled"{{if .TeamInfoDownloadEnabled}} checked{{end}}>
|
||||
<input type="checkbox" name="TBADownloadEnabled"{{if .TBADownloadEnabled}} checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-9 control-label">Enable Automatic Team Accomplishments Download (From TBA)</label>
|
||||
<div class="col-lg-1 checkbox">
|
||||
<input type="checkbox" name="TBADownloadEnabled"{{if .TBAAwardsDownloadEnabled}} checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<form class="form-horizontal" action="/setup/teams" method="POST">
|
||||
<fieldset>
|
||||
<legend>Import Teams</legend>
|
||||
{{if not .EventSettings.TBADownloadEnabled}}<p>To automatically download data about teams, enable TBA Team Info Download on the settings page</p>{{end}}
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="10" name="teamNumbers"
|
||||
placeholder="One team number per line"></textarea>
|
||||
|
||||
Reference in New Issue
Block a user