mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 13:46:44 -04:00
Add TBA API for team info and awards
This commit is contained in:
@@ -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);
|
|
||||||
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,29 +6,28 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
type EventSettings struct {
|
type EventSettings struct {
|
||||||
Id int
|
Id int
|
||||||
Name string
|
Name string
|
||||||
Code string
|
Code string
|
||||||
DisplayBackgroundColor string
|
DisplayBackgroundColor string
|
||||||
NumElimAlliances int
|
NumElimAlliances int
|
||||||
SelectionRound2Order string
|
SelectionRound2Order string
|
||||||
SelectionRound3Order string
|
SelectionRound3Order string
|
||||||
FMSAPIDownloadEnabled bool
|
TBADownloadEnabled bool
|
||||||
FMSAPIUsername string
|
TBAAwardsDownloadEnabled bool
|
||||||
FMSAPIAuthKey string
|
AllianceDisplayHotGoals bool
|
||||||
AllianceDisplayHotGoals bool
|
RedGoalLightsAddress string
|
||||||
RedGoalLightsAddress string
|
BlueGoalLightsAddress string
|
||||||
BlueGoalLightsAddress string
|
TbaPublishingEnabled bool
|
||||||
TbaPublishingEnabled bool
|
TbaEventCode string
|
||||||
TbaEventCode string
|
TbaSecretId string
|
||||||
TbaSecretId string
|
TbaSecret string
|
||||||
TbaSecret string
|
NetworkSecurityEnabled bool
|
||||||
NetworkSecurityEnabled bool
|
ApAddress string
|
||||||
ApAddress string
|
ApUsername string
|
||||||
ApUsername string
|
ApPassword string
|
||||||
ApPassword string
|
SwitchAddress string
|
||||||
SwitchAddress string
|
SwitchPassword string
|
||||||
SwitchPassword string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const eventSettingsId = 0
|
const eventSettingsId = 0
|
||||||
@@ -44,7 +43,8 @@ func (database *Database) GetEventSettings() (*EventSettings, error) {
|
|||||||
eventSettings.NumElimAlliances = 8
|
eventSettings.NumElimAlliances = 8
|
||||||
eventSettings.SelectionRound2Order = "L"
|
eventSettings.SelectionRound2Order = "L"
|
||||||
eventSettings.SelectionRound3Order = ""
|
eventSettings.SelectionRound3Order = ""
|
||||||
eventSettings.FMSAPIDownloadEnabled = false
|
eventSettings.TBADownloadEnabled = true
|
||||||
|
eventSettings.TBAAwardsDownloadEnabled = true
|
||||||
err = database.eventSettingsMap.Insert(eventSettings)
|
err = database.eventSettingsMap.Insert(eventSettings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func TestEventSettingsReadWrite(t *testing.T) {
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, EventSettings{Id: 0, Name: "Untitled Event", Code: "UE", DisplayBackgroundColor: "#00ff00",
|
assert.Equal(t, EventSettings{Id: 0, Name: "Untitled Event", Code: "UE", DisplayBackgroundColor: "#00ff00",
|
||||||
NumElimAlliances: 8, SelectionRound2Order: "L", SelectionRound3Order: "",
|
NumElimAlliances: 8, SelectionRound2Order: "L", SelectionRound3Order: "",
|
||||||
FMSAPIDownloadEnabled: false}, *eventSettings)
|
TBADownloadEnabled: true, TBAAwardsDownloadEnabled: true}, *eventSettings)
|
||||||
|
|
||||||
eventSettings.Name = "Chezy Champs"
|
eventSettings.Name = "Chezy Champs"
|
||||||
eventSettings.Code = "cc"
|
eventSettings.Code = "cc"
|
||||||
|
|||||||
@@ -42,9 +42,8 @@ func SettingsPostHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
eventSettings.NumElimAlliances = numAlliances
|
eventSettings.NumElimAlliances = numAlliances
|
||||||
eventSettings.SelectionRound2Order = r.PostFormValue("selectionRound2Order")
|
eventSettings.SelectionRound2Order = r.PostFormValue("selectionRound2Order")
|
||||||
eventSettings.SelectionRound3Order = r.PostFormValue("selectionRound3Order")
|
eventSettings.SelectionRound3Order = r.PostFormValue("selectionRound3Order")
|
||||||
eventSettings.FMSAPIDownloadEnabled = r.PostFormValue("FMSAPIDownloadEnabled") == "on"
|
eventSettings.TBADownloadEnabled = r.PostFormValue("TBADownloadEnabled") == "on"
|
||||||
eventSettings.FMSAPIUsername = r.PostFormValue("FMSAPIUsername")
|
eventSettings.TBAAwardsDownloadEnabled = r.PostFormValue("TBAAwardsDownloadEnabled") == "on"
|
||||||
eventSettings.FMSAPIAuthKey = r.PostFormValue("FMSAPIAuthKey")
|
|
||||||
eventSettings.AllianceDisplayHotGoals = r.PostFormValue("allianceDisplayHotGoals") == "on"
|
eventSettings.AllianceDisplayHotGoals = r.PostFormValue("allianceDisplayHotGoals") == "on"
|
||||||
eventSettings.RedGoalLightsAddress = r.PostFormValue("redGoalLightsAddress")
|
eventSettings.RedGoalLightsAddress = r.PostFormValue("redGoalLightsAddress")
|
||||||
eventSettings.BlueGoalLightsAddress = r.PostFormValue("blueGoalLightsAddress")
|
eventSettings.BlueGoalLightsAddress = r.PostFormValue("blueGoalLightsAddress")
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
"bytes"
|
||||||
"github.com/dchest/uniuri"
|
"github.com/dchest/uniuri"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
|
|
||||||
const wpaKeyLength = 8
|
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.
|
// Shows the team list.
|
||||||
func TeamsGetHandler(w http.ResponseWriter, r *http.Request) {
|
func TeamsGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -233,71 +233,39 @@ func canModifyTeamList() bool {
|
|||||||
func getOfficialTeamInfo(teamId int) (*Team, error) {
|
func getOfficialTeamInfo(teamId int) (*Team, error) {
|
||||||
// Create the team variable that stores the result
|
// Create the team variable that stores the result
|
||||||
var team Team
|
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 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 != "" {
|
if eventSettings.TBADownloadEnabled {
|
||||||
// Make an HTTP GET request with basic auth to get the info
|
var tbaTeam *TbaTeam = getTeamFromTba(teamId)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the response and handle errors
|
// Check if the result is valid. If a team is not found, just return a basic team
|
||||||
defer resp.Body.Close()
|
if tbaTeam == nil {
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
team = Team{Id: teamId}
|
||||||
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}
|
|
||||||
return &team, nil
|
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 recentAwards []TbaAward;
|
||||||
var name string
|
if eventSettings.TBAAwardsDownloadEnabled {
|
||||||
var nickname string
|
recentAwards = getTeamAwardsFromTba(teamId)
|
||||||
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 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
|
// Use those variables to make a team object
|
||||||
team = Team{Id: teamId, Name: name, Nickname: nickname,
|
team = Team{Id: teamId, Name: tbaTeam.Name, Nickname: tbaTeam.Nickname,
|
||||||
City: city, StateProv: stateProv,
|
City: tbaTeam.Locality, StateProv: tbaTeam.Reigon,
|
||||||
Country: country, RookieYear: rookieYear,
|
Country: tbaTeam.Country, RookieYear: tbaTeam.RookieYear, Accomplishments: accomplishmentsBuffer.String()}
|
||||||
RobotName: robotName}
|
} else {
|
||||||
} else {
|
// If team grab is disabled, just use the team number
|
||||||
// If team grab is disabled, just use the team number
|
team = Team{Id: teamId}
|
||||||
team = Team{Id: teamId}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Return the team object
|
// Return the team object
|
||||||
return &team, nil
|
return &team, nil
|
||||||
|
|||||||
75
tba.go
75
tba.go
@@ -17,6 +17,8 @@ import (
|
|||||||
|
|
||||||
var tbaBaseUrl = "http://www.thebluealliance.com"
|
var tbaBaseUrl = "http://www.thebluealliance.com"
|
||||||
|
|
||||||
|
// MODELS
|
||||||
|
|
||||||
type TbaMatch struct {
|
type TbaMatch struct {
|
||||||
CompLevel string `json:"comp_level"`
|
CompLevel string `json:"comp_level"`
|
||||||
SetNumber int `json:"set_number"`
|
SetNumber int `json:"set_number"`
|
||||||
@@ -49,6 +51,65 @@ type TbaRanking struct {
|
|||||||
GoalFoul int `json:"G&F"`
|
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.
|
// Uploads the event team list to The Blue Alliance.
|
||||||
func PublishTeams() error {
|
func PublishTeams() error {
|
||||||
teams, err := db.GetAllTeams()
|
teams, err := db.GetAllTeams()
|
||||||
@@ -213,6 +274,8 @@ func getTbaTeam(team int) string {
|
|||||||
return fmt.Sprintf("frc%d", team)
|
return fmt.Sprintf("frc%d", team)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HELPERS
|
||||||
|
|
||||||
// Signs the request and sends it to the TBA API.
|
// Signs the request and sends it to the TBA API.
|
||||||
func postTbaRequest(resource string, body []byte) (*http.Response, error) {
|
func postTbaRequest(resource string, body []byte) (*http.Response, error) {
|
||||||
path := fmt.Sprintf("/api/trusted/v1/event/%s/%s/update", eventSettings.TbaEventCode, resource)
|
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)
|
request.Header.Add("X-TBA-Auth-Sig", signature)
|
||||||
return client.Do(request)
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -93,23 +93,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>FRC Events API</legend>
|
<legend>Automatic Team Info Download</legend>
|
||||||
<div class="form-group">
|
<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">
|
<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>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-lg-5 control-label">FRC Events API Username</label>
|
<label class="col-lg-9 control-label">Enable Automatic Team Accomplishments Download (From TBA)</label>
|
||||||
<div class="col-lg-7">
|
<div class="col-lg-1 checkbox">
|
||||||
<input type="text" class="form-control" name="FMSAPIUsername" value="{{.FMSAPIUsername}}">
|
<input type="checkbox" name="TBADownloadEnabled"{{if .TBAAwardsDownloadEnabled}} checked{{end}}>
|
||||||
</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}}">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<form class="form-horizontal" action="/setup/teams" method="POST">
|
<form class="form-horizontal" action="/setup/teams" method="POST">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Import Teams</legend>
|
<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">
|
<div class="form-group">
|
||||||
<textarea class="form-control" rows="10" name="teamNumbers"
|
<textarea class="form-control" rows="10" name="teamNumbers"
|
||||||
placeholder="One team number per line"></textarea>
|
placeholder="One team number per line"></textarea>
|
||||||
|
|||||||
Reference in New Issue
Block a user