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 { |
| 16 | ApiKey string `json:"api_key"` |
| 17 | BaseUrl string `json:"base_url"` |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 18 | } |
| 19 | |
Yash Chainani | 4e2b646 | 2022-03-26 15:23:17 -0700 | [diff] [blame^] | 20 | // 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] | 21 | // Also takes in a file path to the JSON config file that contains your TBA API key. |
| 22 | // It defaults to <workspace root>/config.json |
| 23 | // the config is expected to have the following contents: |
| 24 | //{ |
| 25 | // api_key:"myTBAapiKey" |
| 26 | //} |
Yash Chainani | 4e2b646 | 2022-03-26 15:23:17 -0700 | [diff] [blame^] | 27 | func getJson(year int32, eventCode, configPath, category string) ([]byte, error) { |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 28 | if configPath == "" { |
| 29 | configPath = os.Getenv("BUILD_WORKSPACE_DIRECTORY") + "/scouting_config.json" |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 30 | } |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 31 | |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 32 | // Takes the filepath and grabs the api key from the json. |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 33 | content, err := ioutil.ReadFile(configPath) |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 34 | if err != nil { |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 35 | 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] | 36 | } |
| 37 | // Parses the JSON parameters into a struct. |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 38 | var config scrapingConfig |
| 39 | if err := json.Unmarshal([]byte(content), &config); err != nil { |
| 40 | return nil, errors.New(fmt.Sprint("Failed to parse config file as JSON: ", err)) |
| 41 | } |
| 42 | |
| 43 | // Perform some basic validation on the data. |
| 44 | if config.ApiKey == "" { |
| 45 | return nil, errors.New("Missing 'api_key' in config JSON.") |
| 46 | } |
| 47 | if config.BaseUrl == "" { |
| 48 | config.BaseUrl = "https://www.thebluealliance.com" |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 49 | } |
| 50 | |
| 51 | // Create the TBA event key for the year and event code. |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 52 | eventKey := strconv.Itoa(int(year)) + eventCode |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 53 | |
| 54 | // Create a get request for the match info. |
Yash Chainani | 4e2b646 | 2022-03-26 15:23:17 -0700 | [diff] [blame^] | 55 | 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] | 56 | if err != nil { |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 57 | return nil, errors.New(fmt.Sprint("Failed to build http request: ", err)) |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 58 | } |
| 59 | |
| 60 | // Add the auth key header to the request. |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 61 | req.Header.Add("X-TBA-Auth-Key", config.ApiKey) |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 62 | |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 63 | // Make the API request. |
| 64 | client := &http.Client{} |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 65 | resp, err := client.Do(req) |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 66 | if err != nil { |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 67 | 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] | 68 | } |
| 69 | |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 70 | defer resp.Body.Close() |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 71 | if resp.StatusCode != 200 { |
| 72 | return nil, errors.New(fmt.Sprint("Got unexpected status code from TBA API request: ", resp.Status)) |
| 73 | } |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 74 | |
| 75 | // Get all bytes from response body. |
| 76 | bodyBytes, err := ioutil.ReadAll(resp.Body) |
| 77 | if err != nil { |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 78 | 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] | 79 | } |
| 80 | |
Yash Chainani | 4e2b646 | 2022-03-26 15:23:17 -0700 | [diff] [blame^] | 81 | return bodyBytes, nil |
| 82 | } |
| 83 | |
| 84 | // Return all matches in event according to TBA |
| 85 | func AllMatches(year int32, eventCode, configPath string) ([]Match, error) { |
| 86 | bodyBytes, err := getJson(year, eventCode, configPath, "matches") |
| 87 | |
| 88 | if err != nil { |
| 89 | return nil, err |
| 90 | } |
| 91 | |
Het Satasiya | c6df332 | 2022-02-05 14:12:30 -0800 | [diff] [blame] | 92 | var matches []Match |
| 93 | // Unmarshal json into go usable format. |
Philipp Schrader | d3fac19 | 2022-03-02 20:35:46 -0800 | [diff] [blame] | 94 | if err := json.Unmarshal([]byte(bodyBytes), &matches); err != nil { |
| 95 | 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] | 96 | } |
| 97 | |
| 98 | return matches, nil |
| 99 | } |
Yash Chainani | 4e2b646 | 2022-03-26 15:23:17 -0700 | [diff] [blame^] | 100 | |
| 101 | // Return event rankings according to TBA |
| 102 | func AllRankings(year int32, eventCode, configPath string) (EventRanking, error) { |
| 103 | bodyBytes, err := getJson(year, eventCode, configPath, "rankings") |
| 104 | |
| 105 | if err != nil { |
| 106 | return EventRanking{}, err |
| 107 | } |
| 108 | |
| 109 | var rankings EventRanking |
| 110 | // Unmarshal json into go usable format. |
| 111 | if err := json.Unmarshal([]byte(bodyBytes), &rankings); err != nil { |
| 112 | return EventRanking{}, errors.New(fmt.Sprint("Failed to parse JSON received from TBA: ", err)) |
| 113 | } |
| 114 | |
| 115 | return rankings, nil |
| 116 | } |