blob: 9cb23368502018b214900300a200824c96386f58 [file] [log] [blame]
Het Satasiyac6df3322022-02-05 14:12:30 -08001package scraping
2
3// A library to grab match data from The Blue Alliance.
4import (
5 "encoding/json"
6 "errors"
Philipp Schraderd3fac192022-03-02 20:35:46 -08007 "fmt"
Het Satasiyac6df3322022-02-05 14:12:30 -08008 "io/ioutil"
Het Satasiyac6df3322022-02-05 14:12:30 -08009 "net/http"
10 "os"
Philipp Schraderd3fac192022-03-02 20:35:46 -080011 "strconv"
Het Satasiyac6df3322022-02-05 14:12:30 -080012)
13
14// Stores the TBA API key to access the API.
Philipp Schraderd3fac192022-03-02 20:35:46 -080015type scrapingConfig struct {
Yash Chainani37261d42022-04-19 16:21:27 -070016 ApiKey string `json:"api_key"`
17 BaseUrl string `json:"base_url"`
18 Year int32 `json:"year"`
19 EventCode string `json:"event_code"`
Het Satasiyac6df3322022-02-05 14:12:30 -080020}
21
Yash Chainani4e2b6462022-03-26 15:23:17 -070022// Takes in year and FIRST event code and returns requested information according to TBA.
Het Satasiyac6df3322022-02-05 14:12:30 -080023// Also takes in a file path to the JSON config file that contains your TBA API key.
24// It defaults to <workspace root>/config.json
25// the config is expected to have the following contents:
Philipp Schrader35bb1532023-03-05 13:49:12 -080026//
27// {
28// api_key:"myTBAapiKey"
29// }
Yash Chainani4e2b6462022-03-26 15:23:17 -070030func getJson(year int32, eventCode, configPath, category string) ([]byte, error) {
Philipp Schraderd3fac192022-03-02 20:35:46 -080031 if configPath == "" {
32 configPath = os.Getenv("BUILD_WORKSPACE_DIRECTORY") + "/scouting_config.json"
Het Satasiyac6df3322022-02-05 14:12:30 -080033 }
Philipp Schraderd3fac192022-03-02 20:35:46 -080034
Het Satasiyac6df3322022-02-05 14:12:30 -080035 // Takes the filepath and grabs the api key from the json.
Philipp Schraderd3fac192022-03-02 20:35:46 -080036 content, err := ioutil.ReadFile(configPath)
Het Satasiyac6df3322022-02-05 14:12:30 -080037 if err != nil {
Philipp Schraderd3fac192022-03-02 20:35:46 -080038 return nil, errors.New(fmt.Sprint("Failed to open config at ", configPath, ": ", err))
Het Satasiyac6df3322022-02-05 14:12:30 -080039 }
40 // Parses the JSON parameters into a struct.
Philipp Schraderd3fac192022-03-02 20:35:46 -080041 var config scrapingConfig
42 if err := json.Unmarshal([]byte(content), &config); err != nil {
43 return nil, errors.New(fmt.Sprint("Failed to parse config file as JSON: ", err))
44 }
45
46 // Perform some basic validation on the data.
47 if config.ApiKey == "" {
48 return nil, errors.New("Missing 'api_key' in config JSON.")
49 }
50 if config.BaseUrl == "" {
51 config.BaseUrl = "https://www.thebluealliance.com"
Het Satasiyac6df3322022-02-05 14:12:30 -080052 }
Yash Chainani37261d42022-04-19 16:21:27 -070053 if config.Year == 0 {
54 config.Year = year
55 }
56 if config.EventCode == "" {
57 config.EventCode = eventCode
58 }
Het Satasiyac6df3322022-02-05 14:12:30 -080059
60 // Create the TBA event key for the year and event code.
Yash Chainani37261d42022-04-19 16:21:27 -070061 eventKey := strconv.Itoa(int(config.Year)) + config.EventCode
Het Satasiyac6df3322022-02-05 14:12:30 -080062
63 // Create a get request for the match info.
Yash Chainani4e2b6462022-03-26 15:23:17 -070064 req, err := http.NewRequest("GET", config.BaseUrl+"/api/v3/event/"+eventKey+"/"+category, nil)
Het Satasiyac6df3322022-02-05 14:12:30 -080065 if err != nil {
Philipp Schraderd3fac192022-03-02 20:35:46 -080066 return nil, errors.New(fmt.Sprint("Failed to build http request: ", err))
Het Satasiyac6df3322022-02-05 14:12:30 -080067 }
68
69 // Add the auth key header to the request.
Philipp Schraderd3fac192022-03-02 20:35:46 -080070 req.Header.Add("X-TBA-Auth-Key", config.ApiKey)
Het Satasiyac6df3322022-02-05 14:12:30 -080071
Philipp Schraderd3fac192022-03-02 20:35:46 -080072 // Make the API request.
73 client := &http.Client{}
Het Satasiyac6df3322022-02-05 14:12:30 -080074 resp, err := client.Do(req)
Het Satasiyac6df3322022-02-05 14:12:30 -080075 if err != nil {
Philipp Schraderd3fac192022-03-02 20:35:46 -080076 return nil, errors.New(fmt.Sprint("Failed to make TBA API request: ", err))
Het Satasiyac6df3322022-02-05 14:12:30 -080077 }
78
Het Satasiyac6df3322022-02-05 14:12:30 -080079 defer resp.Body.Close()
Philipp Schraderd3fac192022-03-02 20:35:46 -080080 if resp.StatusCode != 200 {
81 return nil, errors.New(fmt.Sprint("Got unexpected status code from TBA API request: ", resp.Status))
82 }
Het Satasiyac6df3322022-02-05 14:12:30 -080083
84 // Get all bytes from response body.
85 bodyBytes, err := ioutil.ReadAll(resp.Body)
86 if err != nil {
Philipp Schraderd3fac192022-03-02 20:35:46 -080087 return nil, errors.New(fmt.Sprint("Failed to read TBA API response: ", err))
Het Satasiyac6df3322022-02-05 14:12:30 -080088 }
89
Yash Chainani4e2b6462022-03-26 15:23:17 -070090 return bodyBytes, nil
91}
92
Philipp Schraderc49eaf72023-02-26 16:56:52 -080093func GetAllData[T interface{}](year int32, eventCode, configPath string, category string) (T, error) {
94 var result T
95 bodyBytes, err := getJson(year, eventCode, configPath, category)
Yash Chainani4e2b6462022-03-26 15:23:17 -070096 if err != nil {
Philipp Schraderc49eaf72023-02-26 16:56:52 -080097 return result, err
Yash Chainani4e2b6462022-03-26 15:23:17 -070098 }
99
Philipp Schraderc49eaf72023-02-26 16:56:52 -0800100 // Unmarshal the JSON data into the in-memory format.
101 if err = json.Unmarshal([]byte(bodyBytes), &result); err != nil {
102 return result, errors.New(fmt.Sprint("Failed to parse ", category, " JSON received from TBA: ", err))
Het Satasiyac6df3322022-02-05 14:12:30 -0800103 }
104
Philipp Schraderc49eaf72023-02-26 16:56:52 -0800105 return result, nil
Yash Chainani4e2b6462022-03-26 15:23:17 -0700106}