blob: fab725c727b7c1361be60f4bc914048e0347c83e [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"
Emily Markovabf24c9e2023-02-08 20:31:11 -080010 "sort"
Philipp Schraderd3fac192022-03-02 20:35:46 -080011 "strconv"
12 "strings"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080013
Philipp Schrader8747f1b2022-02-23 23:56:22 -080014 "github.com/frc971/971-Robot-Code/scouting/db"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080015 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
Filip Kujawaf882e022022-12-14 13:14:08 -080016 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_driver_rankings"
17 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_driver_rankings_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"
Filip Kujawaf882e022022-12-14 13:14:08 -080020 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_notes"
21 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_notes_response"
Philipp Schraderacf96232022-03-01 22:03:30 -080022 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting"
23 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting_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"
Milo Lin1d59f0c2022-06-22 20:30:58 -070026 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_shift_schedule"
27 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_shift_schedule_response"
Sabina Leaver759090b2023-01-14 20:42:56 -080028 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_actions"
29 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_actions_response"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080030 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting"
Philipp Schrader30005e42022-03-06 13:53:58 -080031 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
Filip Kujawa210a03b2022-11-24 14:41:11 -080032 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_driver_ranking"
33 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_driver_ranking_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070034 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes"
35 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes_response"
Milo Lin1d59f0c2022-06-22 20:30:58 -070036 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_shift_schedule"
37 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_shift_schedule_response"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080038 "github.com/frc971/971-Robot-Code/scouting/webserver/server"
39 flatbuffers "github.com/google/flatbuffers/go"
40)
41
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080042type SubmitDataScouting = submit_data_scouting.SubmitDataScouting
Philipp Schrader30005e42022-03-06 13:53:58 -080043type SubmitDataScoutingResponseT = submit_data_scouting_response.SubmitDataScoutingResponseT
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080044type RequestAllMatches = request_all_matches.RequestAllMatches
45type RequestAllMatchesResponseT = request_all_matches_response.RequestAllMatchesResponseT
Filip Kujawaf882e022022-12-14 13:14:08 -080046type RequestAllDriverRankings = request_all_driver_rankings.RequestAllDriverRankings
47type RequestAllDriverRankingsResponseT = request_all_driver_rankings_response.RequestAllDriverRankingsResponseT
48type RequestAllNotes = request_all_notes.RequestAllNotes
49type RequestAllNotesResponseT = request_all_notes_response.RequestAllNotesResponseT
Philipp Schraderacf96232022-03-01 22:03:30 -080050type RequestDataScouting = request_data_scouting.RequestDataScouting
51type RequestDataScoutingResponseT = request_data_scouting_response.RequestDataScoutingResponseT
Alex Perry81f96ba2022-03-13 18:26:19 -070052type SubmitNotes = submit_notes.SubmitNotes
53type SubmitNotesResponseT = submit_notes_response.SubmitNotesResponseT
54type RequestNotesForTeam = request_notes_for_team.RequestNotesForTeam
55type RequestNotesForTeamResponseT = request_notes_for_team_response.RequestNotesForTeamResponseT
Milo Lin1d59f0c2022-06-22 20:30:58 -070056type RequestShiftSchedule = request_shift_schedule.RequestShiftSchedule
57type RequestShiftScheduleResponseT = request_shift_schedule_response.RequestShiftScheduleResponseT
58type SubmitShiftSchedule = submit_shift_schedule.SubmitShiftSchedule
59type SubmitShiftScheduleResponseT = submit_shift_schedule_response.SubmitShiftScheduleResponseT
Filip Kujawa210a03b2022-11-24 14:41:11 -080060type SubmitDriverRanking = submit_driver_ranking.SubmitDriverRanking
61type SubmitDriverRankingResponseT = submit_driver_ranking_response.SubmitDriverRankingResponseT
Sabina Leaver759090b2023-01-14 20:42:56 -080062type SubmitActions = submit_actions.SubmitActions
63type SubmitActionsResponseT = submit_actions_response.SubmitActionsResponseT
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080064
Philipp Schrader8747f1b2022-02-23 23:56:22 -080065// The interface we expect the database abstraction to conform to.
66// We use an interface here because it makes unit testing easier.
67type Database interface {
Emily Markovabf24c9e2023-02-08 20:31:11 -080068 AddToMatch(db.TeamMatch) error
Milo Lin1d59f0c2022-06-22 20:30:58 -070069 AddToShift(db.Shift) error
Philipp Schrader8747f1b2022-02-23 23:56:22 -080070 AddToStats(db.Stats) error
Emily Markovabf24c9e2023-02-08 20:31:11 -080071 ReturnMatches() ([]db.TeamMatch, error)
Filip Kujawaf882e022022-12-14 13:14:08 -080072 ReturnAllNotes() ([]db.NotesData, error)
73 ReturnAllDriverRankings() ([]db.DriverRankingData, error)
Milo Lin1d59f0c2022-06-22 20:30:58 -070074 ReturnAllShifts() ([]db.Shift, error)
Philipp Schrader8747f1b2022-02-23 23:56:22 -080075 ReturnStats() ([]db.Stats, error)
Milo Lin1d59f0c2022-06-22 20:30:58 -070076 QueryAllShifts(int) ([]db.Shift, error)
Philipp Schrader8747f1b2022-02-23 23:56:22 -080077 QueryStats(int) ([]db.Stats, error)
Philipp Schradereecb8962022-06-01 21:02:42 -070078 QueryNotes(int32) ([]string, error)
Filip Kujawaf947cb42022-11-21 10:00:30 -080079 AddNotes(db.NotesData) error
Filip Kujawa210a03b2022-11-24 14:41:11 -080080 AddDriverRanking(db.DriverRankingData) error
Philipp Schrader8747f1b2022-02-23 23:56:22 -080081}
82
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080083// Handles unknown requests. Just returns a 404.
84func unknown(w http.ResponseWriter, req *http.Request) {
85 w.WriteHeader(http.StatusNotFound)
86}
87
88func respondWithError(w http.ResponseWriter, statusCode int, errorMessage string) {
89 builder := flatbuffers.NewBuilder(1024)
90 builder.Finish((&error_response.ErrorResponseT{
91 ErrorMessage: errorMessage,
92 }).Pack(builder))
93 w.WriteHeader(statusCode)
94 w.Write(builder.FinishedBytes())
95}
96
97func respondNotImplemented(w http.ResponseWriter) {
98 respondWithError(w, http.StatusNotImplemented, "")
99}
100
Philipp Schraderb7e75932022-03-26 16:18:34 -0700101func parseRequest[T interface{}](w http.ResponseWriter, buf []byte, requestName string, parser func([]byte, flatbuffers.UOffsetT) *T) (*T, bool) {
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800102 success := true
103 defer func() {
104 if r := recover(); r != nil {
Philipp Schraderb7e75932022-03-26 16:18:34 -0700105 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse %s: %v", requestName, r))
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800106 success = false
107 }
108 }()
Philipp Schraderb7e75932022-03-26 16:18:34 -0700109 result := parser(buf, 0)
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800110 return result, success
111}
112
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700113// Parses the authorization information that the browser inserts into the
114// headers. The authorization follows this format:
115//
116// req.Headers["Authorization"] = []string{"Basic <base64 encoded username:password>"}
117func parseUsername(req *http.Request) string {
118 auth, ok := req.Header["Authorization"]
119 if !ok {
120 return "unknown"
121 }
122
123 parts := strings.Split(auth[0], " ")
124 if !(len(parts) == 2 && parts[0] == "Basic") {
125 return "unknown"
126 }
127
128 info, err := base64.StdEncoding.DecodeString(parts[1])
129 if err != nil {
130 log.Println("ERROR: Failed to parse Basic authentication.")
131 return "unknown"
132 }
133
134 loginParts := strings.Split(string(info), ":")
135 if len(loginParts) != 2 {
136 return "unknown"
137 }
138 return loginParts[0]
139}
140
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800141// Handles a SubmitDataScouting request.
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800142type submitDataScoutingHandler struct {
143 db Database
144}
145
146func (handler submitDataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700147 // Get the username of the person submitting the data.
148 username := parseUsername(req)
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700149
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800150 requestBytes, err := io.ReadAll(req.Body)
151 if err != nil {
152 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
153 return
154 }
155
Philipp Schraderb7e75932022-03-26 16:18:34 -0700156 request, success := parseRequest[SubmitDataScouting](w, requestBytes, "SubmitDataScouting", submit_data_scouting.GetRootAsSubmitDataScouting)
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800157 if !success {
158 return
159 }
160
Philipp Schraderd7b6eba2022-03-18 22:21:25 -0700161 log.Println("Got data scouting data for match", request.Match(), "team", request.Team(), "from", username)
162
Philipp Schrader30005e42022-03-06 13:53:58 -0800163 stats := db.Stats{
Philipp Schraderfee07e12022-03-17 22:19:47 -0700164 TeamNumber: request.Team(),
165 MatchNumber: request.Match(),
Philipp Schrader30b4a682022-04-16 14:36:17 -0700166 SetNumber: request.SetNumber(),
Philipp Schrader4535b7e2022-04-08 20:27:00 -0700167 CompLevel: string(request.CompLevel()),
Philipp Schraderfee07e12022-03-17 22:19:47 -0700168 StartingQuadrant: request.StartingQuadrant(),
169 AutoBallPickedUp: [5]bool{
170 request.AutoBall1(), request.AutoBall2(), request.AutoBall3(),
171 request.AutoBall4(), request.AutoBall5(),
172 },
Philipp Schraderfa45d742022-03-18 19:29:05 -0700173 ShotsMissedAuto: request.MissedShotsAuto(),
174 UpperGoalAuto: request.UpperGoalAuto(),
175 LowerGoalAuto: request.LowerGoalAuto(),
176 ShotsMissed: request.MissedShotsTele(),
177 UpperGoalShots: request.UpperGoalTele(),
178 LowerGoalShots: request.LowerGoalTele(),
179 PlayedDefense: request.DefenseRating(),
180 DefenseReceivedScore: request.DefenseReceivedRating(),
181 Climbing: int32(request.ClimbLevel()),
182 CollectedBy: username,
183 Comment: string(request.Comment()),
Philipp Schrader30005e42022-03-06 13:53:58 -0800184 }
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800185
Philipp Schraderfee07e12022-03-17 22:19:47 -0700186 // Do some error checking.
187 if stats.StartingQuadrant < 1 || stats.StartingQuadrant > 4 {
188 respondWithError(w, http.StatusBadRequest, fmt.Sprint(
189 "Invalid starting_quadrant field value of ", stats.StartingQuadrant))
190 return
191 }
192
Philipp Schrader30005e42022-03-06 13:53:58 -0800193 err = handler.db.AddToStats(stats)
194 if err != nil {
195 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to submit datascouting data: ", err))
Philipp Schraderfee07e12022-03-17 22:19:47 -0700196 return
Philipp Schrader30005e42022-03-06 13:53:58 -0800197 }
198
199 builder := flatbuffers.NewBuilder(50 * 1024)
200 builder.Finish((&SubmitDataScoutingResponseT{}).Pack(builder))
201 w.Write(builder.FinishedBytes())
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800202}
203
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800204// Handles a RequestAllMaches request.
205type requestAllMatchesHandler struct {
206 db Database
207}
208
Emily Markovabf24c9e2023-02-08 20:31:11 -0800209func findIndexInList(list []string, comp_level string) (int, error) {
210 for index, value := range list {
211 if value == comp_level {
212 return index, nil
213 }
214 }
215 return -1, errors.New(fmt.Sprint("Failed to find comp level ", comp_level, " in list ", list))
216}
217
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800218func (handler requestAllMatchesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
219 requestBytes, err := io.ReadAll(req.Body)
220 if err != nil {
221 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
222 return
223 }
224
Philipp Schraderb7e75932022-03-26 16:18:34 -0700225 _, success := parseRequest(w, requestBytes, "RequestAllMatches", request_all_matches.GetRootAsRequestAllMatches)
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800226 if !success {
227 return
228 }
229
230 matches, err := handler.db.ReturnMatches()
231 if err != nil {
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700232 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
Philipp Schrader2e7eb0002022-03-02 22:52:39 -0800233 return
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800234 }
235
Emily Markovabf24c9e2023-02-08 20:31:11 -0800236 // Change structure of match objects in the database(1 per team) to
237 // the old match structure(1 per match) that the webserver uses.
238 type Key struct {
239 MatchNumber int32
240 SetNumber int32
241 CompLevel string
242 }
243
244 assembledMatches := map[Key]request_all_matches_response.MatchT{}
245
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800246 for _, match := range matches {
Emily Markovabf24c9e2023-02-08 20:31:11 -0800247 key := Key{match.MatchNumber, match.SetNumber, match.CompLevel}
Emily Markovabf24c9e2023-02-08 20:31:11 -0800248 entry, ok := assembledMatches[key]
249 if !ok {
250 entry = request_all_matches_response.MatchT{
251 MatchNumber: match.MatchNumber,
252 SetNumber: match.SetNumber,
253 CompLevel: match.CompLevel,
254 }
255 }
Emily Markovabf24c9e2023-02-08 20:31:11 -0800256 switch match.Alliance {
257 case "R":
258 switch match.AlliancePosition {
259 case 1:
260 entry.R1 = match.TeamNumber
261 case 2:
262 entry.R2 = match.TeamNumber
263 case 3:
264 entry.R3 = match.TeamNumber
265 default:
266 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown red position ", strconv.Itoa(int(match.AlliancePosition)), " in match ", strconv.Itoa(int(match.MatchNumber))))
267 return
268 }
269 case "B":
270 switch match.AlliancePosition {
271 case 1:
272 entry.B1 = match.TeamNumber
273 case 2:
274 entry.B2 = match.TeamNumber
275 case 3:
276 entry.B3 = match.TeamNumber
277 default:
278 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown blue position ", strconv.Itoa(int(match.AlliancePosition)), " in match ", strconv.Itoa(int(match.MatchNumber))))
279 return
280 }
281 default:
282 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown alliance ", match.Alliance, " in match ", strconv.Itoa(int(match.AlliancePosition))))
283 return
284 }
285 assembledMatches[key] = entry
286 }
287
288 var response RequestAllMatchesResponseT
289 for _, match := range assembledMatches {
290 copied_match := match
291 response.MatchList = append(response.MatchList, &copied_match)
292 }
293
294 var MATCH_TYPE_ORDERING = []string{"qm", "ef", "qf", "sf", "f"}
295
296 err = nil
297 sort.Slice(response.MatchList, func(i, j int) bool {
298 if err != nil {
299 return false
300 }
301 a := response.MatchList[i]
302 b := response.MatchList[j]
303
Emily Markovaabcac6e2023-02-18 17:50:03 -0800304 aMatchTypeIndex, err2 := findIndexInList(MATCH_TYPE_ORDERING, a.CompLevel)
305 if err2 != nil {
306 err = errors.New(fmt.Sprint("Comp level ", a.CompLevel, " not found in sorting list ", MATCH_TYPE_ORDERING, " : ", err2))
Emily Markovabf24c9e2023-02-08 20:31:11 -0800307 return false
308 }
Emily Markovaabcac6e2023-02-18 17:50:03 -0800309 bMatchTypeIndex, err2 := findIndexInList(MATCH_TYPE_ORDERING, b.CompLevel)
310 if err2 != nil {
311 err = errors.New(fmt.Sprint("Comp level ", b.CompLevel, " not found in sorting list ", MATCH_TYPE_ORDERING, " : ", err2))
Emily Markovabf24c9e2023-02-08 20:31:11 -0800312 return false
313 }
314
315 if aMatchTypeIndex < bMatchTypeIndex {
316 return true
317 }
318 if aMatchTypeIndex > bMatchTypeIndex {
319 return false
320 }
321
322 // Then sort by match number. E.g. in semi finals, all match 1 rounds
323 // are done first. Then come match 2 rounds. And then, if necessary,
324 // the match 3 rounds.
325 aMatchNumber := a.MatchNumber
326 bMatchNumber := b.MatchNumber
327 if aMatchNumber < bMatchNumber {
328 return true
329 }
330 if aMatchNumber > bMatchNumber {
331 return false
332 }
333 // Lastly, sort by set number. I.e. Semi Final 1 Match 1 happens first.
334 // Then comes Semi Final 2 Match 1. Then comes Semi Final 1 Match 2. Then
335 // Semi Final 2 Match 2.
336 aSetNumber := a.SetNumber
337 bSetNumber := b.SetNumber
338 if aSetNumber < bSetNumber {
339 return true
340 }
341 if aSetNumber > bSetNumber {
342 return false
343 }
344 return true
345 })
346
347 if err != nil {
348 // check if error happened during sorting and notify webpage if that
349 respondWithError(w, http.StatusInternalServerError, fmt.Sprint(err))
350 return
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800351 }
352
353 builder := flatbuffers.NewBuilder(50 * 1024)
354 builder.Finish((&response).Pack(builder))
355 w.Write(builder.FinishedBytes())
356}
357
Philipp Schraderacf96232022-03-01 22:03:30 -0800358// Handles a RequestDataScouting request.
359type requestDataScoutingHandler struct {
360 db Database
361}
362
363func (handler requestDataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
364 requestBytes, err := io.ReadAll(req.Body)
365 if err != nil {
366 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
367 return
368 }
369
Philipp Schraderb7e75932022-03-26 16:18:34 -0700370 _, success := parseRequest(w, requestBytes, "RequestDataScouting", request_data_scouting.GetRootAsRequestDataScouting)
Philipp Schraderacf96232022-03-01 22:03:30 -0800371 if !success {
372 return
373 }
374
375 stats, err := handler.db.ReturnStats()
376 if err != nil {
377 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Faled to query database: ", err))
Philipp Schrader2e7eb0002022-03-02 22:52:39 -0800378 return
Philipp Schraderacf96232022-03-01 22:03:30 -0800379 }
380
381 var response RequestDataScoutingResponseT
382 for _, stat := range stats {
383 response.StatsList = append(response.StatsList, &request_data_scouting_response.StatsT{
Philipp Schraderfa45d742022-03-18 19:29:05 -0700384 Team: stat.TeamNumber,
385 Match: stat.MatchNumber,
Philipp Schrader30b4a682022-04-16 14:36:17 -0700386 SetNumber: stat.SetNumber,
Philipp Schrader4535b7e2022-04-08 20:27:00 -0700387 CompLevel: stat.CompLevel,
Philipp Schraderfa45d742022-03-18 19:29:05 -0700388 StartingQuadrant: stat.StartingQuadrant,
389 AutoBall1: stat.AutoBallPickedUp[0],
390 AutoBall2: stat.AutoBallPickedUp[1],
391 AutoBall3: stat.AutoBallPickedUp[2],
392 AutoBall4: stat.AutoBallPickedUp[3],
393 AutoBall5: stat.AutoBallPickedUp[4],
394 MissedShotsAuto: stat.ShotsMissedAuto,
395 UpperGoalAuto: stat.UpperGoalAuto,
396 LowerGoalAuto: stat.LowerGoalAuto,
397 MissedShotsTele: stat.ShotsMissed,
398 UpperGoalTele: stat.UpperGoalShots,
399 LowerGoalTele: stat.LowerGoalShots,
400 DefenseRating: stat.PlayedDefense,
401 DefenseReceivedRating: stat.DefenseReceivedScore,
402 ClimbLevel: request_data_scouting_response.ClimbLevel(stat.Climbing),
403 CollectedBy: stat.CollectedBy,
404 Comment: stat.Comment,
Philipp Schraderacf96232022-03-01 22:03:30 -0800405 })
406 }
407
408 builder := flatbuffers.NewBuilder(50 * 1024)
409 builder.Finish((&response).Pack(builder))
410 w.Write(builder.FinishedBytes())
411}
412
Alex Perry81f96ba2022-03-13 18:26:19 -0700413type submitNoteScoutingHandler struct {
414 db Database
415}
416
417func (handler submitNoteScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
418 requestBytes, err := io.ReadAll(req.Body)
419 if err != nil {
420 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
421 return
422 }
423
Philipp Schraderb7e75932022-03-26 16:18:34 -0700424 request, success := parseRequest(w, requestBytes, "SubmitNotes", submit_notes.GetRootAsSubmitNotes)
Alex Perry81f96ba2022-03-13 18:26:19 -0700425 if !success {
426 return
427 }
428
Filip Kujawaf947cb42022-11-21 10:00:30 -0800429 err = handler.db.AddNotes(db.NotesData{
430 TeamNumber: request.Team(),
431 Notes: string(request.Notes()),
432 GoodDriving: bool(request.GoodDriving()),
433 BadDriving: bool(request.BadDriving()),
434 SketchyClimb: bool(request.SketchyClimb()),
435 SolidClimb: bool(request.SolidClimb()),
436 GoodDefense: bool(request.GoodDefense()),
437 BadDefense: bool(request.BadDefense()),
438 })
Alex Perry81f96ba2022-03-13 18:26:19 -0700439 if err != nil {
440 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert notes: %v", err))
441 return
442 }
443
444 var response SubmitNotesResponseT
445 builder := flatbuffers.NewBuilder(10)
446 builder.Finish((&response).Pack(builder))
447 w.Write(builder.FinishedBytes())
448}
449
Alex Perry81f96ba2022-03-13 18:26:19 -0700450type requestNotesForTeamHandler struct {
451 db Database
452}
453
454func (handler requestNotesForTeamHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
455 requestBytes, err := io.ReadAll(req.Body)
456 if err != nil {
457 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
458 return
459 }
460
Philipp Schraderb7e75932022-03-26 16:18:34 -0700461 request, success := parseRequest(w, requestBytes, "RequestNotesForTeam", request_notes_for_team.GetRootAsRequestNotesForTeam)
Alex Perry81f96ba2022-03-13 18:26:19 -0700462 if !success {
463 return
464 }
465
Philipp Schradereecb8962022-06-01 21:02:42 -0700466 notes, err := handler.db.QueryNotes(request.Team())
Alex Perry81f96ba2022-03-13 18:26:19 -0700467 if err != nil {
468 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query notes: %v", err))
469 return
470 }
471
472 var response RequestNotesForTeamResponseT
Philipp Schradereecb8962022-06-01 21:02:42 -0700473 for _, data := range notes {
Alex Perry81f96ba2022-03-13 18:26:19 -0700474 response.Notes = append(response.Notes, &request_notes_for_team_response.NoteT{data})
475 }
476
477 builder := flatbuffers.NewBuilder(1024)
478 builder.Finish((&response).Pack(builder))
479 w.Write(builder.FinishedBytes())
480}
481
Milo Lin1d59f0c2022-06-22 20:30:58 -0700482type requestShiftScheduleHandler struct {
483 db Database
484}
485
486func (handler requestShiftScheduleHandler) 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 _, success := parseRequest(w, requestBytes, "RequestShiftSchedule", request_shift_schedule.GetRootAsRequestShiftSchedule)
494 if !success {
495 return
496 }
497
498 shiftData, err := handler.db.ReturnAllShifts()
499 if err != nil {
500 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query shift schedule: %v", err))
501 return
502 }
503
504 var response RequestShiftScheduleResponseT
505 for _, shifts := range shiftData {
506 response.ShiftSchedule = append(response.ShiftSchedule, &request_shift_schedule_response.MatchAssignmentT{
507 MatchNumber: shifts.MatchNumber,
508 R1scouter: shifts.R1scouter,
509 R2scouter: shifts.R2scouter,
510 R3scouter: shifts.R3scouter,
511 B1scouter: shifts.B1scouter,
512 B2scouter: shifts.B2scouter,
513 B3scouter: shifts.B3scouter,
514 })
515 }
516
517 builder := flatbuffers.NewBuilder(1024)
518 builder.Finish((&response).Pack(builder))
519 w.Write(builder.FinishedBytes())
520}
521
522type submitShiftScheduleHandler struct {
523 db Database
524}
525
526func (handler submitShiftScheduleHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
527 // Get the username of the person submitting the data.
528 username := parseUsername(req)
529
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 := parseRequest[SubmitShiftSchedule](w, requestBytes, "SubmitShiftSchedule", submit_shift_schedule.GetRootAsSubmitShiftSchedule)
537 if !success {
538 return
539 }
540
541 log.Println("Got shift schedule from", username)
542 shift_schedule_length := request.ShiftScheduleLength()
543 for i := 0; i < shift_schedule_length; i++ {
544 var match_assignment submit_shift_schedule.MatchAssignment
545 request.ShiftSchedule(&match_assignment, i)
546 current_shift := db.Shift{
547 MatchNumber: match_assignment.MatchNumber(),
548 R1scouter: string(match_assignment.R1scouter()),
549 R2scouter: string(match_assignment.R2scouter()),
550 R3scouter: string(match_assignment.R3scouter()),
551 B1scouter: string(match_assignment.B1scouter()),
552 B2scouter: string(match_assignment.B2scouter()),
553 B3scouter: string(match_assignment.B3scouter()),
554 }
555 err = handler.db.AddToShift(current_shift)
556 if err != nil {
557 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to submit shift schedule: ", err))
558 return
559 }
560 }
561
562 builder := flatbuffers.NewBuilder(50 * 1024)
563 builder.Finish((&SubmitShiftScheduleResponseT{}).Pack(builder))
564 w.Write(builder.FinishedBytes())
565}
566
Filip Kujawa210a03b2022-11-24 14:41:11 -0800567type SubmitDriverRankingHandler struct {
568 db Database
569}
570
571func (handler SubmitDriverRankingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
572 requestBytes, err := io.ReadAll(req.Body)
573 if err != nil {
574 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
575 return
576 }
577
578 request, success := parseRequest(w, requestBytes, "SubmitDriverRanking", submit_driver_ranking.GetRootAsSubmitDriverRanking)
579 if !success {
580 return
581 }
582
583 err = handler.db.AddDriverRanking(db.DriverRankingData{
584 MatchNumber: request.MatchNumber(),
585 Rank1: request.Rank1(),
586 Rank2: request.Rank2(),
587 Rank3: request.Rank3(),
588 })
589
590 if err != nil {
591 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert driver ranking: %v", err))
592 return
593 }
594
595 var response SubmitDriverRankingResponseT
596 builder := flatbuffers.NewBuilder(10)
597 builder.Finish((&response).Pack(builder))
598 w.Write(builder.FinishedBytes())
599}
600
Filip Kujawaf882e022022-12-14 13:14:08 -0800601type requestAllNotesHandler struct {
602 db Database
603}
604
605func (handler requestAllNotesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
606 requestBytes, err := io.ReadAll(req.Body)
607 if err != nil {
608 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
609 return
610 }
611
612 _, success := parseRequest(w, requestBytes, "RequestAllNotes", request_all_notes.GetRootAsRequestAllNotes)
613 if !success {
614 return
615 }
616
617 notes, err := handler.db.ReturnAllNotes()
618 if err != nil {
619 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
620 return
621 }
622
623 var response RequestAllNotesResponseT
624 for _, note := range notes {
625 response.NoteList = append(response.NoteList, &request_all_notes_response.NoteT{
626 Team: note.TeamNumber,
627 Notes: note.Notes,
628 GoodDriving: note.GoodDriving,
629 BadDriving: note.BadDriving,
630 SketchyClimb: note.SketchyClimb,
631 SolidClimb: note.SolidClimb,
632 GoodDefense: note.GoodDefense,
633 BadDefense: note.BadDefense,
634 })
635 }
636
637 builder := flatbuffers.NewBuilder(50 * 1024)
638 builder.Finish((&response).Pack(builder))
639 w.Write(builder.FinishedBytes())
640}
641
642type requestAllDriverRankingsHandler struct {
643 db Database
644}
645
646func (handler requestAllDriverRankingsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
647 requestBytes, err := io.ReadAll(req.Body)
648 if err != nil {
649 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
650 return
651 }
652
653 _, success := parseRequest(w, requestBytes, "RequestAllDriverRankings", request_all_driver_rankings.GetRootAsRequestAllDriverRankings)
654 if !success {
655 return
656 }
657
658 rankings, err := handler.db.ReturnAllDriverRankings()
659 if err != nil {
660 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
661 return
662 }
663
664 var response RequestAllDriverRankingsResponseT
665 for _, ranking := range rankings {
666 response.DriverRankingList = append(response.DriverRankingList, &request_all_driver_rankings_response.RankingT{
667 MatchNumber: ranking.MatchNumber,
668 Rank1: ranking.Rank1,
669 Rank2: ranking.Rank2,
670 Rank3: ranking.Rank3,
671 })
672 }
673
674 builder := flatbuffers.NewBuilder(50 * 1024)
675 builder.Finish((&response).Pack(builder))
676 w.Write(builder.FinishedBytes())
677}
678
Philipp Schrader43c730b2023-02-26 20:27:44 -0800679func HandleRequests(db Database, scoutingServer server.ScoutingServer) {
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800680 scoutingServer.HandleFunc("/requests", unknown)
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800681 scoutingServer.Handle("/requests/submit/data_scouting", submitDataScoutingHandler{db})
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800682 scoutingServer.Handle("/requests/request/all_matches", requestAllMatchesHandler{db})
Filip Kujawaf882e022022-12-14 13:14:08 -0800683 scoutingServer.Handle("/requests/request/all_notes", requestAllNotesHandler{db})
684 scoutingServer.Handle("/requests/request/all_driver_rankings", requestAllDriverRankingsHandler{db})
Philipp Schraderacf96232022-03-01 22:03:30 -0800685 scoutingServer.Handle("/requests/request/data_scouting", requestDataScoutingHandler{db})
Alex Perry81f96ba2022-03-13 18:26:19 -0700686 scoutingServer.Handle("/requests/submit/submit_notes", submitNoteScoutingHandler{db})
687 scoutingServer.Handle("/requests/request/notes_for_team", requestNotesForTeamHandler{db})
Milo Lin1d59f0c2022-06-22 20:30:58 -0700688 scoutingServer.Handle("/requests/submit/shift_schedule", submitShiftScheduleHandler{db})
689 scoutingServer.Handle("/requests/request/shift_schedule", requestShiftScheduleHandler{db})
Filip Kujawa210a03b2022-11-24 14:41:11 -0800690 scoutingServer.Handle("/requests/submit/submit_driver_ranking", SubmitDriverRankingHandler{db})
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800691}