Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 1 | package scraping |
| 2 | |
| 3 | // A library to grab match data from The Blue Alliance. |
| 4 | import ( |
| 5 | "encoding/json" |
| 6 | "errors" |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 7 | "fmt" |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 8 | "io/ioutil" |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 9 | "net/http" |
| 10 | "os" |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 11 | "strconv" |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 12 | ) |
| 13 | |
| 14 | // Stores the TBA API key to access the API. |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 15 | type scrapingConfig struct { |
Yash Chainani | 37261d4 | 2022-04-19 16:21:27 -0700 | [diff] [blame] | 16 | ApiKey string `json:"api_key"` |
| 17 | BaseUrl string `json:"base_url"` |
| 18 | Year int32 `json:"year"` |
| 19 | EventCode string `json:"event_code"` |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 20 | } |
| 21 | |
Yash Chainani | 4e2b646 | 2022-03-26 15:23:17 -0700 | [diff] [blame] | 22 | // Takes in year and FIRST event code and returns requested information according to TBA. |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 23 | // 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: |
| 26 | //{ |
| 27 | // api_key:"myTBAapiKey" |
| 28 | //} |
Yash Chainani | 4e2b646 | 2022-03-26 15:23:17 -0700 | [diff] [blame] | 29 | func getJson(year int32, eventCode, configPath, category string) ([]byte, error) { |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 30 | if configPath == "" { |
| 31 | configPath = os.Getenv("BUILD_WORKSPACE_DIRECTORY") + "/scouting_config.json" |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 32 | } |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 33 | |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 34 | // Takes the filepath and grabs the api key from the json. |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 35 | content, err := ioutil.ReadFile(configPath) |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 36 | if err != nil { |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 37 | return nil, errors.New(fmt.Sprint("Failed to open config at ", configPath, ": ", err)) |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 38 | } |
| 39 | // Parses the JSON parameters into a struct. |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 40 | var config scrapingConfig |
| 41 | if err := json.Unmarshal([]byte(content), &config); err != nil { |
| 42 | return nil, errors.New(fmt.Sprint("Failed to parse config file as JSON: ", err)) |
| 43 | } |
| 44 | |
| 45 | // Perform some basic validation on the data. |
| 46 | if config.ApiKey == "" { |
| 47 | return nil, errors.New("Missing 'api_key' in config JSON.") |
| 48 | } |
| 49 | if config.BaseUrl == "" { |
| 50 | config.BaseUrl = "https://www.thebluealliance.com" |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 51 | } |
Yash Chainani | 37261d4 | 2022-04-19 16:21:27 -0700 | [diff] [blame] | 52 | if config.Year == 0 { |
| 53 | config.Year = year |
| 54 | } |
| 55 | if config.EventCode == "" { |
| 56 | config.EventCode = eventCode |
| 57 | } |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 58 | |
| 59 | // Create the TBA event key for the year and event code. |
Yash Chainani | 37261d4 | 2022-04-19 16:21:27 -0700 | [diff] [blame] | 60 | eventKey := strconv.Itoa(int(config.Year)) + config.EventCode |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 61 | |
| 62 | // Create a get request for the match info. |
Yash Chainani | 4e2b646 | 2022-03-26 15:23:17 -0700 | [diff] [blame] | 63 | req, err := http.NewRequest("GET", config.BaseUrl+"/api/v3/event/"+eventKey+"/"+category, nil) |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 64 | if err != nil { |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 65 | return nil, errors.New(fmt.Sprint("Failed to build http request: ", err)) |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 66 | } |
| 67 | |
| 68 | // Add the auth key header to the request. |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 69 | req.Header.Add("X-TBA-Auth-Key", config.ApiKey) |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 70 | |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 71 | // Make the API request. |
| 72 | client := &http.Client{} |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 73 | resp, err := client.Do(req) |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 74 | if err != nil { |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 75 | return nil, errors.New(fmt.Sprint("Failed to make TBA API request: ", err)) |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 76 | } |
| 77 | |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 78 | defer resp.Body.Close() |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 79 | if resp.StatusCode != 200 { |
| 80 | return nil, errors.New(fmt.Sprint("Got unexpected status code from TBA API request: ", resp.Status)) |
| 81 | } |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 82 | |
| 83 | // Get all bytes from response body. |
| 84 | bodyBytes, err := ioutil.ReadAll(resp.Body) |
| 85 | if err != nil { |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 86 | return nil, errors.New(fmt.Sprint("Failed to read TBA API response: ", err)) |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 87 | } |
| 88 | |
Yash Chainani | 4e2b646 | 2022-03-26 15:23:17 -0700 | [diff] [blame] | 89 | return bodyBytes, nil |
| 90 | } |
| 91 | |
| 92 | // Return all matches in event according to TBA |
| 93 | func AllMatches(year int32, eventCode, configPath string) ([]Match, error) { |
| 94 | bodyBytes, err := getJson(year, eventCode, configPath, "matches") |
| 95 | |
| 96 | if err != nil { |
| 97 | return nil, err |
| 98 | } |
| 99 | |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 100 | var matches []Match |
| 101 | // Unmarshal json into go usable format. |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 102 | if err := json.Unmarshal([]byte(bodyBytes), &matches); err != nil { |
| 103 | return nil, errors.New(fmt.Sprint("Failed to parse JSON received from TBA: ", err)) |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 104 | } |
| 105 | |
| 106 | return matches, nil |
| 107 | } |
Yash Chainani | 4e2b646 | 2022-03-26 15:23:17 -0700 | [diff] [blame] | 108 | |
| 109 | // Return event rankings according to TBA |
| 110 | func AllRankings(year int32, eventCode, configPath string) (EventRanking, error) { |
| 111 | bodyBytes, err := getJson(year, eventCode, configPath, "rankings") |
| 112 | |
| 113 | if err != nil { |
| 114 | return EventRanking{}, err |
| 115 | } |
| 116 | |
| 117 | var rankings EventRanking |
| 118 | // Unmarshal json into go usable format. |
| 119 | if err := json.Unmarshal([]byte(bodyBytes), &rankings); err != nil { |
| 120 | return EventRanking{}, errors.New(fmt.Sprint("Failed to parse JSON received from TBA: ", err)) |
| 121 | } |
| 122 | |
| 123 | return rankings, nil |
| 124 | } |