blob: c9a688084bb647a9cb6322c01c46f09633bf804d [file] [log] [blame]
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08001package requests
2
3import (
Philipp Schraderfae8a7e2022-03-13 22:51:54 -07004 "encoding/base64"
Philipp Schraderd3fac192022-03-02 20:35:46 -08005 "errors"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08006 "fmt"
7 "io"
Philipp Schraderfae8a7e2022-03-13 22:51:54 -07008 "log"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08009 "net/http"
Philipp Schraderd3fac192022-03-02 20:35:46 -080010 "strconv"
11 "strings"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080012
Philipp Schrader8747f1b2022-02-23 23:56:22 -080013 "github.com/frc971/971-Robot-Code/scouting/db"
Philipp Schraderd3fac192022-03-02 20:35:46 -080014 "github.com/frc971/971-Robot-Code/scouting/scraping"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080015 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
Philipp Schraderd3fac192022-03-02 20:35:46 -080016 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/refresh_match_list"
17 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/refresh_match_list_response"
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080018 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches"
19 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches_response"
Philipp Schraderacf96232022-03-01 22:03:30 -080020 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting"
21 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting_response"
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080022 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team"
23 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070024 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team"
25 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team_response"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080026 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting"
Philipp Schrader30005e42022-03-06 13:53:58 -080027 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070028 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes"
29 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes_response"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080030 "github.com/frc971/971-Robot-Code/scouting/webserver/server"
31 flatbuffers "github.com/google/flatbuffers/go"
32)
33
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080034type SubmitDataScouting = submit_data_scouting.SubmitDataScouting
Philipp Schrader30005e42022-03-06 13:53:58 -080035type SubmitDataScoutingResponseT = submit_data_scouting_response.SubmitDataScoutingResponseT
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080036type RequestAllMatches = request_all_matches.RequestAllMatches
37type RequestAllMatchesResponseT = request_all_matches_response.RequestAllMatchesResponseT
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080038type RequestMatchesForTeam = request_matches_for_team.RequestMatchesForTeam
39type RequestMatchesForTeamResponseT = request_matches_for_team_response.RequestMatchesForTeamResponseT
Philipp Schraderacf96232022-03-01 22:03:30 -080040type RequestDataScouting = request_data_scouting.RequestDataScouting
41type RequestDataScoutingResponseT = request_data_scouting_response.RequestDataScoutingResponseT
Philipp Schraderd3fac192022-03-02 20:35:46 -080042type RefreshMatchList = refresh_match_list.RefreshMatchList
43type RefreshMatchListResponseT = refresh_match_list_response.RefreshMatchListResponseT
Alex Perry81f96ba2022-03-13 18:26:19 -070044type SubmitNotes = submit_notes.SubmitNotes
45type SubmitNotesResponseT = submit_notes_response.SubmitNotesResponseT
46type RequestNotesForTeam = request_notes_for_team.RequestNotesForTeam
47type RequestNotesForTeamResponseT = request_notes_for_team_response.RequestNotesForTeamResponseT
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080048
Philipp Schrader8747f1b2022-02-23 23:56:22 -080049// The interface we expect the database abstraction to conform to.
50// We use an interface here because it makes unit testing easier.
51type Database interface {
52 AddToMatch(db.Match) error
53 AddToStats(db.Stats) error
54 ReturnMatches() ([]db.Match, error)
55 ReturnStats() ([]db.Stats, error)
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080056 QueryMatches(int32) ([]db.Match, error)
Philipp Schrader8747f1b2022-02-23 23:56:22 -080057 QueryStats(int) ([]db.Stats, error)
Alex Perry81f96ba2022-03-13 18:26:19 -070058 QueryNotes(int32) (db.NotesData, error)
59 AddNotes(db.NotesData) error
Philipp Schrader8747f1b2022-02-23 23:56:22 -080060}
61
Philipp Schraderd3fac192022-03-02 20:35:46 -080062type ScrapeMatchList func(int32, string) ([]scraping.Match, error)
63
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080064// Handles unknown requests. Just returns a 404.
65func unknown(w http.ResponseWriter, req *http.Request) {
66 w.WriteHeader(http.StatusNotFound)
67}
68
69func respondWithError(w http.ResponseWriter, statusCode int, errorMessage string) {
70 builder := flatbuffers.NewBuilder(1024)
71 builder.Finish((&error_response.ErrorResponseT{
72 ErrorMessage: errorMessage,
73 }).Pack(builder))
74 w.WriteHeader(statusCode)
75 w.Write(builder.FinishedBytes())
76}
77
78func respondNotImplemented(w http.ResponseWriter) {
79 respondWithError(w, http.StatusNotImplemented, "")
80}
81
82// TODO(phil): Can we turn this into a generic?
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080083func parseSubmitDataScouting(w http.ResponseWriter, buf []byte) (*SubmitDataScouting, bool) {
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080084 success := true
85 defer func() {
86 if r := recover(); r != nil {
87 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
88 success = false
89 }
90 }()
91 result := submit_data_scouting.GetRootAsSubmitDataScouting(buf, 0)
92 return result, success
93}
94
Philipp Schraderfae8a7e2022-03-13 22:51:54 -070095// Parses the authorization information that the browser inserts into the
96// headers. The authorization follows this format:
97//
98// req.Headers["Authorization"] = []string{"Basic <base64 encoded username:password>"}
99func parseUsername(req *http.Request) string {
100 auth, ok := req.Header["Authorization"]
101 if !ok {
102 return "unknown"
103 }
104
105 parts := strings.Split(auth[0], " ")
106 if !(len(parts) == 2 && parts[0] == "Basic") {
107 return "unknown"
108 }
109
110 info, err := base64.StdEncoding.DecodeString(parts[1])
111 if err != nil {
112 log.Println("ERROR: Failed to parse Basic authentication.")
113 return "unknown"
114 }
115
116 loginParts := strings.Split(string(info), ":")
117 if len(loginParts) != 2 {
118 return "unknown"
119 }
120 return loginParts[0]
121}
122
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800123// Handles a SubmitDataScouting request.
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800124type submitDataScoutingHandler struct {
125 db Database
126}
127
128func (handler submitDataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700129 // Get the username of the person submitting the data.
130 username := parseUsername(req)
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700131
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800132 requestBytes, err := io.ReadAll(req.Body)
133 if err != nil {
134 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
135 return
136 }
137
Philipp Schrader30005e42022-03-06 13:53:58 -0800138 request, success := parseSubmitDataScouting(w, requestBytes)
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800139 if !success {
140 return
141 }
142
Philipp Schraderd7b6eba2022-03-18 22:21:25 -0700143 log.Println("Got data scouting data for match", request.Match(), "team", request.Team(), "from", username)
144
Philipp Schrader30005e42022-03-06 13:53:58 -0800145 stats := db.Stats{
Philipp Schraderfee07e12022-03-17 22:19:47 -0700146 TeamNumber: request.Team(),
147 MatchNumber: request.Match(),
148 StartingQuadrant: request.StartingQuadrant(),
149 AutoBallPickedUp: [5]bool{
150 request.AutoBall1(), request.AutoBall2(), request.AutoBall3(),
151 request.AutoBall4(), request.AutoBall5(),
152 },
Philipp Schraderfa45d742022-03-18 19:29:05 -0700153 ShotsMissedAuto: request.MissedShotsAuto(),
154 UpperGoalAuto: request.UpperGoalAuto(),
155 LowerGoalAuto: request.LowerGoalAuto(),
156 ShotsMissed: request.MissedShotsTele(),
157 UpperGoalShots: request.UpperGoalTele(),
158 LowerGoalShots: request.LowerGoalTele(),
159 PlayedDefense: request.DefenseRating(),
160 DefenseReceivedScore: request.DefenseReceivedRating(),
161 Climbing: int32(request.ClimbLevel()),
162 CollectedBy: username,
163 Comment: string(request.Comment()),
Philipp Schrader30005e42022-03-06 13:53:58 -0800164 }
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800165
Philipp Schraderfee07e12022-03-17 22:19:47 -0700166 // Do some error checking.
167 if stats.StartingQuadrant < 1 || stats.StartingQuadrant > 4 {
168 respondWithError(w, http.StatusBadRequest, fmt.Sprint(
169 "Invalid starting_quadrant field value of ", stats.StartingQuadrant))
170 return
171 }
172
Philipp Schrader30005e42022-03-06 13:53:58 -0800173 err = handler.db.AddToStats(stats)
174 if err != nil {
175 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to submit datascouting data: ", err))
Philipp Schraderfee07e12022-03-17 22:19:47 -0700176 return
Philipp Schrader30005e42022-03-06 13:53:58 -0800177 }
178
179 builder := flatbuffers.NewBuilder(50 * 1024)
180 builder.Finish((&SubmitDataScoutingResponseT{}).Pack(builder))
181 w.Write(builder.FinishedBytes())
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800182}
183
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800184// TODO(phil): Can we turn this into a generic?
185func parseRequestAllMatches(w http.ResponseWriter, buf []byte) (*RequestAllMatches, bool) {
186 success := true
187 defer func() {
188 if r := recover(); r != nil {
189 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
190 success = false
191 }
192 }()
193 result := request_all_matches.GetRootAsRequestAllMatches(buf, 0)
194 return result, success
195}
196
197// Handles a RequestAllMaches request.
198type requestAllMatchesHandler struct {
199 db Database
200}
201
202func (handler requestAllMatchesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
203 requestBytes, err := io.ReadAll(req.Body)
204 if err != nil {
205 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
206 return
207 }
208
209 _, success := parseRequestAllMatches(w, requestBytes)
210 if !success {
211 return
212 }
213
214 matches, err := handler.db.ReturnMatches()
215 if err != nil {
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700216 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
Philipp Schrader2e7eb0002022-03-02 22:52:39 -0800217 return
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800218 }
219
220 var response RequestAllMatchesResponseT
221 for _, match := range matches {
222 response.MatchList = append(response.MatchList, &request_all_matches_response.MatchT{
223 MatchNumber: match.MatchNumber,
224 Round: match.Round,
225 CompLevel: match.CompLevel,
226 R1: match.R1,
227 R2: match.R2,
228 R3: match.R3,
229 B1: match.B1,
230 B2: match.B2,
231 B3: match.B3,
232 })
233 }
234
235 builder := flatbuffers.NewBuilder(50 * 1024)
236 builder.Finish((&response).Pack(builder))
237 w.Write(builder.FinishedBytes())
238}
239
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800240// TODO(phil): Can we turn this into a generic?
241func parseRequestMatchesForTeam(w http.ResponseWriter, buf []byte) (*RequestMatchesForTeam, bool) {
242 success := true
243 defer func() {
244 if r := recover(); r != nil {
245 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
246 success = false
247 }
248 }()
249 result := request_matches_for_team.GetRootAsRequestMatchesForTeam(buf, 0)
250 return result, success
251}
252
253// Handles a RequestMatchesForTeam request.
254type requestMatchesForTeamHandler struct {
255 db Database
256}
257
258func (handler requestMatchesForTeamHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
259 requestBytes, err := io.ReadAll(req.Body)
260 if err != nil {
261 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
262 return
263 }
264
265 request, success := parseRequestMatchesForTeam(w, requestBytes)
266 if !success {
267 return
268 }
269
270 matches, err := handler.db.QueryMatches(request.Team())
271 if err != nil {
272 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Faled to query database: ", err))
Philipp Schrader2e7eb0002022-03-02 22:52:39 -0800273 return
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800274 }
275
276 var response RequestAllMatchesResponseT
277 for _, match := range matches {
278 response.MatchList = append(response.MatchList, &request_all_matches_response.MatchT{
279 MatchNumber: match.MatchNumber,
280 Round: match.Round,
281 CompLevel: match.CompLevel,
282 R1: match.R1,
283 R2: match.R2,
284 R3: match.R3,
285 B1: match.B1,
286 B2: match.B2,
287 B3: match.B3,
288 })
289 }
290
291 builder := flatbuffers.NewBuilder(50 * 1024)
292 builder.Finish((&response).Pack(builder))
293 w.Write(builder.FinishedBytes())
294}
295
Philipp Schraderacf96232022-03-01 22:03:30 -0800296// TODO(phil): Can we turn this into a generic?
297func parseRequestDataScouting(w http.ResponseWriter, buf []byte) (*RequestDataScouting, bool) {
298 success := true
299 defer func() {
300 if r := recover(); r != nil {
301 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
302 success = false
303 }
304 }()
305 result := request_data_scouting.GetRootAsRequestDataScouting(buf, 0)
306 return result, success
307}
308
309// Handles a RequestDataScouting request.
310type requestDataScoutingHandler struct {
311 db Database
312}
313
314func (handler requestDataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
315 requestBytes, err := io.ReadAll(req.Body)
316 if err != nil {
317 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
318 return
319 }
320
321 _, success := parseRequestDataScouting(w, requestBytes)
322 if !success {
323 return
324 }
325
326 stats, err := handler.db.ReturnStats()
327 if err != nil {
328 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Faled to query database: ", err))
Philipp Schrader2e7eb0002022-03-02 22:52:39 -0800329 return
Philipp Schraderacf96232022-03-01 22:03:30 -0800330 }
331
332 var response RequestDataScoutingResponseT
333 for _, stat := range stats {
334 response.StatsList = append(response.StatsList, &request_data_scouting_response.StatsT{
Philipp Schraderfa45d742022-03-18 19:29:05 -0700335 Team: stat.TeamNumber,
336 Match: stat.MatchNumber,
337 StartingQuadrant: stat.StartingQuadrant,
338 AutoBall1: stat.AutoBallPickedUp[0],
339 AutoBall2: stat.AutoBallPickedUp[1],
340 AutoBall3: stat.AutoBallPickedUp[2],
341 AutoBall4: stat.AutoBallPickedUp[3],
342 AutoBall5: stat.AutoBallPickedUp[4],
343 MissedShotsAuto: stat.ShotsMissedAuto,
344 UpperGoalAuto: stat.UpperGoalAuto,
345 LowerGoalAuto: stat.LowerGoalAuto,
346 MissedShotsTele: stat.ShotsMissed,
347 UpperGoalTele: stat.UpperGoalShots,
348 LowerGoalTele: stat.LowerGoalShots,
349 DefenseRating: stat.PlayedDefense,
350 DefenseReceivedRating: stat.DefenseReceivedScore,
351 ClimbLevel: request_data_scouting_response.ClimbLevel(stat.Climbing),
352 CollectedBy: stat.CollectedBy,
353 Comment: stat.Comment,
Philipp Schraderacf96232022-03-01 22:03:30 -0800354 })
355 }
356
357 builder := flatbuffers.NewBuilder(50 * 1024)
358 builder.Finish((&response).Pack(builder))
359 w.Write(builder.FinishedBytes())
360}
361
Philipp Schraderd3fac192022-03-02 20:35:46 -0800362// TODO(phil): Can we turn this into a generic?
363func parseRefreshMatchList(w http.ResponseWriter, buf []byte) (*RefreshMatchList, bool) {
364 success := true
365 defer func() {
366 if r := recover(); r != nil {
367 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse RefreshMatchList: %v", r))
368 success = false
369 }
370 }()
371 result := refresh_match_list.GetRootAsRefreshMatchList(buf, 0)
372 return result, success
373}
374
375func parseTeamKey(teamKey string) (int, error) {
376 // TBA prefixes teams with "frc". Not sure why. Get rid of that.
377 teamKey = strings.TrimPrefix(teamKey, "frc")
378 return strconv.Atoi(teamKey)
379}
380
381// Parses the alliance data from the specified match and returns the three red
382// teams and the three blue teams.
383func parseTeamKeys(match *scraping.Match) ([3]int32, [3]int32, error) {
384 redKeys := match.Alliances.Red.TeamKeys
385 blueKeys := match.Alliances.Blue.TeamKeys
386
387 if len(redKeys) != 3 || len(blueKeys) != 3 {
388 return [3]int32{}, [3]int32{}, errors.New(fmt.Sprintf(
389 "Found %d red teams and %d blue teams.", len(redKeys), len(blueKeys)))
390 }
391
392 var red [3]int32
393 for i, key := range redKeys {
394 team, err := parseTeamKey(key)
395 if err != nil {
396 return [3]int32{}, [3]int32{}, errors.New(fmt.Sprintf(
397 "Failed to parse red %d team '%s' as integer: %v", i+1, key, err))
398 }
399 red[i] = int32(team)
400 }
401 var blue [3]int32
402 for i, key := range blueKeys {
403 team, err := parseTeamKey(key)
404 if err != nil {
405 return [3]int32{}, [3]int32{}, errors.New(fmt.Sprintf(
406 "Failed to parse blue %d team '%s' as integer: %v", i+1, key, err))
407 }
408 blue[i] = int32(team)
409 }
410 return red, blue, nil
411}
412
413type refreshMatchListHandler struct {
414 db Database
415 scrape ScrapeMatchList
416}
417
418func (handler refreshMatchListHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
419 requestBytes, err := io.ReadAll(req.Body)
420 if err != nil {
421 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
422 return
423 }
424
425 request, success := parseRefreshMatchList(w, requestBytes)
426 if !success {
427 return
428 }
429
430 matches, err := handler.scrape(request.Year(), string(request.EventCode()))
431 if err != nil {
432 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Faled to scrape match list: ", err))
433 return
434 }
435
436 for _, match := range matches {
437 // Make sure the data is valid.
438 red, blue, err := parseTeamKeys(&match)
439 if err != nil {
440 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf(
441 "TheBlueAlliance data for match %d is malformed: %v", match.MatchNumber, err))
442 return
443 }
444 // Add the match to the database.
Philipp Schrader7365d322022-03-06 16:40:08 -0800445 err = handler.db.AddToMatch(db.Match{
Philipp Schraderd3fac192022-03-02 20:35:46 -0800446 MatchNumber: int32(match.MatchNumber),
447 // TODO(phil): What does Round mean?
448 Round: 1,
449 CompLevel: match.CompLevel,
450 R1: red[0],
451 R2: red[1],
452 R3: red[2],
453 B1: blue[0],
454 B2: blue[1],
455 B3: blue[2],
456 })
Philipp Schrader7365d322022-03-06 16:40:08 -0800457 if err != nil {
458 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf(
459 "Failed to add match %d to the database: %v", match.MatchNumber, err))
460 return
461 }
Philipp Schraderd3fac192022-03-02 20:35:46 -0800462 }
463
464 var response RefreshMatchListResponseT
465 builder := flatbuffers.NewBuilder(1024)
466 builder.Finish((&response).Pack(builder))
467 w.Write(builder.FinishedBytes())
468}
469
Alex Perry81f96ba2022-03-13 18:26:19 -0700470func parseSubmitNotes(w http.ResponseWriter, buf []byte) (*SubmitNotes, bool) {
471 success := true
472 defer func() {
473 if r := recover(); r != nil {
474 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse RefreshMatchList: %v", r))
475 success = false
476 }
477 }()
478 result := submit_notes.GetRootAsSubmitNotes(buf, 0)
479 return result, success
480}
481
482type submitNoteScoutingHandler struct {
483 db Database
484}
485
486func (handler submitNoteScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
487 requestBytes, err := io.ReadAll(req.Body)
488 if err != nil {
489 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
490 return
491 }
492
493 request, success := parseSubmitNotes(w, requestBytes)
494 if !success {
495 return
496 }
497
498 err = handler.db.AddNotes(db.NotesData{
499 TeamNumber: request.Team(),
500 Notes: []string{string(request.Notes())},
501 })
502 if err != nil {
503 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert notes: %v", err))
504 return
505 }
506
507 var response SubmitNotesResponseT
508 builder := flatbuffers.NewBuilder(10)
509 builder.Finish((&response).Pack(builder))
510 w.Write(builder.FinishedBytes())
511}
512
513func parseRequestNotesForTeam(w http.ResponseWriter, buf []byte) (*RequestNotesForTeam, bool) {
514 success := true
515 defer func() {
516 if r := recover(); r != nil {
517 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse RefreshMatchList: %v", r))
518 success = false
519 }
520 }()
521 result := request_notes_for_team.GetRootAsRequestNotesForTeam(buf, 0)
522 return result, success
523}
524
525type requestNotesForTeamHandler struct {
526 db Database
527}
528
529func (handler requestNotesForTeamHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
530 requestBytes, err := io.ReadAll(req.Body)
531 if err != nil {
532 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
533 return
534 }
535
536 request, success := parseRequestNotesForTeam(w, requestBytes)
537 if !success {
538 return
539 }
540
541 notesData, err := handler.db.QueryNotes(request.Team())
542 if err != nil {
543 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query notes: %v", err))
544 return
545 }
546
547 var response RequestNotesForTeamResponseT
548 for _, data := range notesData.Notes {
549 response.Notes = append(response.Notes, &request_notes_for_team_response.NoteT{data})
550 }
551
552 builder := flatbuffers.NewBuilder(1024)
553 builder.Finish((&response).Pack(builder))
554 w.Write(builder.FinishedBytes())
555}
556
Philipp Schraderd3fac192022-03-02 20:35:46 -0800557func HandleRequests(db Database, scrape ScrapeMatchList, scoutingServer server.ScoutingServer) {
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800558 scoutingServer.HandleFunc("/requests", unknown)
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800559 scoutingServer.Handle("/requests/submit/data_scouting", submitDataScoutingHandler{db})
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800560 scoutingServer.Handle("/requests/request/all_matches", requestAllMatchesHandler{db})
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800561 scoutingServer.Handle("/requests/request/matches_for_team", requestMatchesForTeamHandler{db})
Philipp Schraderacf96232022-03-01 22:03:30 -0800562 scoutingServer.Handle("/requests/request/data_scouting", requestDataScoutingHandler{db})
Philipp Schraderd3fac192022-03-02 20:35:46 -0800563 scoutingServer.Handle("/requests/refresh_match_list", refreshMatchListHandler{db, scrape})
Alex Perry81f96ba2022-03-13 18:26:19 -0700564 scoutingServer.Handle("/requests/submit/submit_notes", submitNoteScoutingHandler{db})
565 scoutingServer.Handle("/requests/request/notes_for_team", requestNotesForTeamHandler{db})
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800566}