Add TBA API for team info and awards

This commit is contained in:
Sam Baumgarten
2015-04-01 14:19:14 -07:00
parent 52c316014b
commit 1774197c13
9 changed files with 142 additions and 107 deletions

View File

@@ -1,4 +0,0 @@
-- +goose Up
ALTER TABLE event_settings ADD COLUMN fmsapidownloadenabled BOOLEAN;
ALTER TABLE event_settings ADD COLUMN fmsapiusername VARCHAR(255);
ALTER TABLE event_settings ADD COLUMN fmsapiauthkey VARCHAR(255);

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,29 +6,28 @@
package main
type EventSettings struct {
Id int
Name string
Code string
DisplayBackgroundColor string
NumElimAlliances int
SelectionRound2Order string
SelectionRound3Order string
FMSAPIDownloadEnabled bool
FMSAPIUsername string
FMSAPIAuthKey string
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
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
}
const eventSettingsId = 0
@@ -44,7 +43,8 @@ func (database *Database) GetEventSettings() (*EventSettings, error) {
eventSettings.NumElimAlliances = 8
eventSettings.SelectionRound2Order = "L"
eventSettings.SelectionRound3Order = ""
eventSettings.FMSAPIDownloadEnabled = false
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: "",
FMSAPIDownloadEnabled: false}, *eventSettings)
TBADownloadEnabled: true, TBAAwardsDownloadEnabled: true}, *eventSettings)
eventSettings.Name = "Chezy Champs"
eventSettings.Code = "cc"

View File

@@ -42,9 +42,8 @@ func SettingsPostHandler(w http.ResponseWriter, r *http.Request) {
eventSettings.NumElimAlliances = numAlliances
eventSettings.SelectionRound2Order = r.PostFormValue("selectionRound2Order")
eventSettings.SelectionRound3Order = r.PostFormValue("selectionRound3Order")
eventSettings.FMSAPIDownloadEnabled = r.PostFormValue("FMSAPIDownloadEnabled") == "on"
eventSettings.FMSAPIUsername = r.PostFormValue("FMSAPIUsername")
eventSettings.FMSAPIAuthKey = r.PostFormValue("FMSAPIAuthKey")
eventSettings.TBADownloadEnabled = r.PostFormValue("TBADownloadEnabled") == "on"
eventSettings.TBAAwardsDownloadEnabled = r.PostFormValue("TBAAwardsDownloadEnabled") == "on"
eventSettings.AllianceDisplayHotGoals = r.PostFormValue("allianceDisplayHotGoals") == "on"
eventSettings.RedGoalLightsAddress = r.PostFormValue("redGoalLightsAddress")
eventSettings.BlueGoalLightsAddress = r.PostFormValue("blueGoalLightsAddress")

View File

@@ -6,12 +6,12 @@
package main
import (
"encoding/json"
"fmt"
"time"
"bytes"
"github.com/dchest/uniuri"
"github.com/gorilla/mux"
"html/template"
"io/ioutil"
"net/http"
"strconv"
"strings"
@@ -19,7 +19,7 @@ import (
const wpaKeyLength = 8
var officialTeamInfoUrl = "https://frc-api.usfirst.org/api/v1.0/teams/2015"
var officialTeamInfoUrl = "http://www.thebluealliance.com/api/v2/team/"
// Shows the team list.
func TeamsGetHandler(w http.ResponseWriter, r *http.Request) {
@@ -233,71 +233,39 @@ func canModifyTeamList() bool {
func getOfficialTeamInfo(teamId int) (*Team, error) {
// Create the team variable that stores the result
var team Team
// 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.FMSAPIDownloadEnabled && eventSettings.FMSAPIUsername != "" && eventSettings.FMSAPIAuthKey != "" {
// Make an HTTP GET request with basic auth to get the info
client := &http.Client{}
var url = officialTeamInfoUrl + "?teamNumber=" + strconv.Itoa(teamId);
req, err := http.NewRequest("GET", url, nil)
req.SetBasicAuth(eventSettings.FMSAPIUsername, eventSettings.FMSAPIAuthKey)
resp, err := client.Do(req)
// Handle any errors
if err != nil {
return nil, err
}
if eventSettings.TBADownloadEnabled {
var tbaTeam *TbaTeam = getTeamFromTba(teamId)
// Get the response and handle errors
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// Parse the response into an object
var respJSON map[string]interface{}
json.Unmarshal(body, &respJSON)
// Check if the result is valid. If a team is not found it won't be allowing us to just return a basic team
if respJSON == nil {
team = Team{Id: 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
}
// Break out teams array
var teams = respJSON["teams"].([]interface{})
// Get the first team returned
var teamData = teams[0].(map[string]interface {})
}
// Put all the info into variables (to allow for validation)
var name string
var nickname string
var city string
var stateProv string
var country string
var rookieYear int
var robotName string
if teamData["nameFull"] != nil { name = teamData["nameFull"].(string) }
if teamData["nameShort"] != nil { nickname = teamData["nameShort"].(string) }
if teamData["city"] != nil { city = teamData["city"].(string) }
if teamData["stateProv"] != nil { stateProv = teamData["stateProv"].(string) }
if teamData["country"] != nil { country = teamData["country"].(string) }
if teamData["rookieYear"] != nil { rookieYear = int(teamData["rookieYear"].(float64)) }
if teamData["robotName"] != nil { robotName = teamData["robotName"].(string) }
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: name, Nickname: nickname,
City: city, StateProv: stateProv,
Country: country, RookieYear: rookieYear,
RobotName: robotName}
} else {
// If team grab is disabled, just use the team number
team = Team{Id: teamId}
}
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 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"`
SetNumber int `json:"set_number"`
@@ -49,6 +51,65 @@ type TbaRanking struct {
GoalFoul int `json:"G&F"`
}
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()
@@ -213,6 +274,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 +290,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

@@ -93,23 +93,17 @@
</div>
</fieldset>
<fieldset>
<legend>FRC Events API</legend>
<legend>Automatic Team Info Download</legend>
<div class="form-group">
<label class="col-lg-7 control-label">Enable FRC Events API Team Info Download</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="FMSAPIDownloadEnabled"{{if .FMSAPIDownloadEnabled}} checked{{end}}>
<input type="checkbox" name="TBADownloadEnabled"{{if .TBADownloadEnabled}} checked{{end}}>
</div>
</div>
<div class="form-group">
<label class="col-lg-5 control-label">FRC Events API Username</label>
<div class="col-lg-7">
<input type="text" class="form-control" name="FMSAPIUsername" value="{{.FMSAPIUsername}}">
</div>
</div>
<div class="form-group">
<label class="col-lg-5 control-label">FRC Events API Authorization Key</label>
<div class="col-lg-7">
<input type="text" class="form-control" name="FMSAPIAuthKey" value="{{.FMSAPIAuthKey}}">
<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,7 +18,7 @@
<form class="form-horizontal" action="/setup/teams" method="POST">
<fieldset>
<legend>Import Teams</legend>
{{if not .EventSettings.FMSAPIDownloadEnabled}}<p>To automatically download data about teams, enter your FRC Events API key on the event settings page</p>{{end}}
{{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>