mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 21:56:50 -04:00
Switch to using the FRC Events API for getting event info rather than scraping usfirst.org (which is down)
This commit is contained in:
4
db/migrations/20150324201112_AddFRCEventsAPIColumns.sql
Normal file
4
db/migrations/20150324201112_AddFRCEventsAPIColumns.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
-- +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);
|
||||
@@ -13,7 +13,9 @@ type EventSettings struct {
|
||||
NumElimAlliances int
|
||||
SelectionRound2Order string
|
||||
SelectionRound3Order string
|
||||
TeamInfoDownloadEnabled bool
|
||||
FMSAPIDownloadEnabled bool
|
||||
FMSAPIUsername string
|
||||
FMSAPIAuthKey string
|
||||
AllianceDisplayHotGoals bool
|
||||
RedGoalLightsAddress string
|
||||
BlueGoalLightsAddress string
|
||||
@@ -42,7 +44,7 @@ func (database *Database) GetEventSettings() (*EventSettings, error) {
|
||||
eventSettings.NumElimAlliances = 8
|
||||
eventSettings.SelectionRound2Order = "L"
|
||||
eventSettings.SelectionRound3Order = ""
|
||||
eventSettings.TeamInfoDownloadEnabled = true
|
||||
eventSettings.FMSAPIDownloadEnabled = false
|
||||
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)
|
||||
FMSAPIDownloadEnabled: false}, *eventSettings)
|
||||
|
||||
eventSettings.Name = "Chezy Champs"
|
||||
eventSettings.Code = "cc"
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"code.google.com/p/gofpdf"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/jung-kurt/gofpdf"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"text/template"
|
||||
|
||||
@@ -42,7 +42,9 @@ 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.FMSAPIDownloadEnabled = r.PostFormValue("FMSAPIDownloadEnabled") == "on"
|
||||
eventSettings.FMSAPIUsername = r.PostFormValue("FMSAPIUsername")
|
||||
eventSettings.FMSAPIAuthKey = r.PostFormValue("FMSAPIAuthKey")
|
||||
eventSettings.AllianceDisplayHotGoals = r.PostFormValue("allianceDisplayHotGoals") == "on"
|
||||
eventSettings.RedGoalLightsAddress = r.PostFormValue("redGoalLightsAddress")
|
||||
eventSettings.BlueGoalLightsAddress = r.PostFormValue("blueGoalLightsAddress")
|
||||
|
||||
114
setup_teams.go
114
setup_teams.go
@@ -6,24 +6,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/dchest/uniuri"
|
||||
"github.com/gorilla/mux"
|
||||
"html"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TeamListings struct {
|
||||
NumberOfTeams int `json:"teamCountTotal"`
|
||||
Teams []JSONTeam `json:"teams"`
|
||||
}
|
||||
|
||||
type JSONTeam struct {
|
||||
ShortName string `json:"nameShort"`
|
||||
TeamNumber string `json:"teamNumber"`
|
||||
LongName string `json:"nameFull"`
|
||||
City string `json:"city"`
|
||||
}
|
||||
|
||||
const wpaKeyLength = 8
|
||||
|
||||
var officialTeamInfoUrl = "https://my.usfirst.org/frc/scoring/index.lasso?page=teamlist"
|
||||
var officialTeamInfo map[int][]string
|
||||
var officialTeamInfoUrl = "https://frc-api.usfirst.org/api/v1.0/teams/2015"
|
||||
|
||||
// Shows the team list.
|
||||
func TeamsGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -235,52 +243,74 @@ 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)
|
||||
// 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
|
||||
}
|
||||
|
||||
// Get the response and handle errors
|
||||
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
|
||||
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
teamData, ok := officialTeamInfo[teamId]
|
||||
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])}
|
||||
|
||||
// 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) }
|
||||
|
||||
// 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 no team data exists, just fill in the team number.
|
||||
team = Team{Id: teamId}
|
||||
// If team grab is disabled, just use the team number
|
||||
team = Team{Id: teamId}
|
||||
}
|
||||
|
||||
// Return the team object
|
||||
return &team, nil
|
||||
}
|
||||
|
||||
@@ -91,10 +91,25 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>FRC Events API</legend>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-7 control-label">Enable Team Info Download From usfirst.org</label>
|
||||
<label class="col-lg-7 control-label">Enable FRC Events API Team Info Download</label>
|
||||
<div class="col-lg-1 checkbox">
|
||||
<input type="checkbox" name="teamInfoDownloadEnabled"{{if .TeamInfoDownloadEnabled}} checked{{end}}>
|
||||
<input type="checkbox" name="FMSAPIDownloadEnabled"{{if .FMSAPIDownloadEnabled}} 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}}">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
@@ -18,6 +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}}
|
||||
<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