Merge branch 'master' into changes_for_2015_game

This commit is contained in:
Patrick Fairbank
2015-08-21 20:57:14 -07:00
11 changed files with 163 additions and 78 deletions

View File

@@ -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

View File

@@ -0,0 +1,3 @@
-- +goose Up
ALTER TABLE event_settings ADD COLUMN tbadownloadenabled BOOLEAN;
ALTER TABLE event_settings ADD COLUMN tbaawardsdownloadenabled BOOLEAN;

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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
View File

@@ -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)
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>