blob: 9628c0dfe15f8a92ad1f2a10aeae3327eb44504b [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"
Filip Kujawac1ded372023-05-27 14:33:43 -070015 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/delete_2023_data_scouting"
16 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/delete_2023_data_scouting_response"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080017 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
Emily Markova290147d2023-03-03 22:40:06 -080018 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_2023_data_scouting"
19 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_2023_data_scouting_response"
Filip Kujawaf882e022022-12-14 13:14:08 -080020 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_driver_rankings"
21 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_driver_rankings_response"
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080022 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches"
23 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches_response"
Filip Kujawaf882e022022-12-14 13:14:08 -080024 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_notes"
25 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_notes_response"
Emily Markova8e39f452023-12-23 12:17:30 -080026 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_pit_images"
27 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_pit_images_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070028 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team"
29 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team_response"
Emily Markovafaecfe12023-07-01 12:40:03 -070030 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_pit_images"
31 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_pit_images_response"
Milo Lin1d59f0c2022-06-22 20:30:58 -070032 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_shift_schedule"
33 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_shift_schedule_response"
Sabina Leaver759090b2023-01-14 20:42:56 -080034 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_actions"
35 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_actions_response"
Filip Kujawa210a03b2022-11-24 14:41:11 -080036 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_driver_ranking"
37 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_driver_ranking_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070038 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes"
39 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes_response"
Emily Markovafaecfe12023-07-01 12:40:03 -070040 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_pit_image"
41 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_pit_image_response"
Milo Lin1d59f0c2022-06-22 20:30:58 -070042 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_shift_schedule"
43 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_shift_schedule_response"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080044 "github.com/frc971/971-Robot-Code/scouting/webserver/server"
45 flatbuffers "github.com/google/flatbuffers/go"
46)
47
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080048type RequestAllMatches = request_all_matches.RequestAllMatches
49type RequestAllMatchesResponseT = request_all_matches_response.RequestAllMatchesResponseT
Filip Kujawaf882e022022-12-14 13:14:08 -080050type RequestAllDriverRankings = request_all_driver_rankings.RequestAllDriverRankings
51type RequestAllDriverRankingsResponseT = request_all_driver_rankings_response.RequestAllDriverRankingsResponseT
52type RequestAllNotes = request_all_notes.RequestAllNotes
53type RequestAllNotesResponseT = request_all_notes_response.RequestAllNotesResponseT
Emily Markova290147d2023-03-03 22:40:06 -080054type Request2023DataScouting = request_2023_data_scouting.Request2023DataScouting
55type Request2023DataScoutingResponseT = request_2023_data_scouting_response.Request2023DataScoutingResponseT
Alex Perry81f96ba2022-03-13 18:26:19 -070056type SubmitNotes = submit_notes.SubmitNotes
57type SubmitNotesResponseT = submit_notes_response.SubmitNotesResponseT
Emily Markovafaecfe12023-07-01 12:40:03 -070058type SubmitPitImage = submit_pit_image.SubmitPitImage
59type SubmitPitImageResponseT = submit_pit_image_response.SubmitPitImageResponseT
60type RequestPitImages = request_pit_images.RequestPitImages
61type RequestPitImagesResponseT = request_pit_images_response.RequestPitImagesResponseT
Emily Markova8e39f452023-12-23 12:17:30 -080062type RequestAllPitImages = request_all_pit_images.RequestAllPitImages
63type RequestAllPitImagesResponseT = request_all_pit_images_response.RequestAllPitImagesResponseT
Alex Perry81f96ba2022-03-13 18:26:19 -070064type RequestNotesForTeam = request_notes_for_team.RequestNotesForTeam
65type RequestNotesForTeamResponseT = request_notes_for_team_response.RequestNotesForTeamResponseT
Milo Lin1d59f0c2022-06-22 20:30:58 -070066type RequestShiftSchedule = request_shift_schedule.RequestShiftSchedule
67type RequestShiftScheduleResponseT = request_shift_schedule_response.RequestShiftScheduleResponseT
68type SubmitShiftSchedule = submit_shift_schedule.SubmitShiftSchedule
69type SubmitShiftScheduleResponseT = submit_shift_schedule_response.SubmitShiftScheduleResponseT
Filip Kujawa210a03b2022-11-24 14:41:11 -080070type SubmitDriverRanking = submit_driver_ranking.SubmitDriverRanking
71type SubmitDriverRankingResponseT = submit_driver_ranking_response.SubmitDriverRankingResponseT
Sabina Leaver759090b2023-01-14 20:42:56 -080072type SubmitActions = submit_actions.SubmitActions
Sabina Leaver9b4eb312023-02-20 19:58:17 -080073type Action = submit_actions.Action
Sabina Leaver759090b2023-01-14 20:42:56 -080074type SubmitActionsResponseT = submit_actions_response.SubmitActionsResponseT
Filip Kujawac1ded372023-05-27 14:33:43 -070075type Delete2023DataScouting = delete_2023_data_scouting.Delete2023DataScouting
76type Delete2023DataScoutingResponseT = delete_2023_data_scouting_response.Delete2023DataScoutingResponseT
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080077
Philipp Schrader8747f1b2022-02-23 23:56:22 -080078// The interface we expect the database abstraction to conform to.
79// We use an interface here because it makes unit testing easier.
80type Database interface {
Emily Markovabf24c9e2023-02-08 20:31:11 -080081 AddToMatch(db.TeamMatch) error
Milo Lin1d59f0c2022-06-22 20:30:58 -070082 AddToShift(db.Shift) error
Emily Markova290147d2023-03-03 22:40:06 -080083 AddToStats2023(db.Stats2023) error
Emily Markovabf24c9e2023-02-08 20:31:11 -080084 ReturnMatches() ([]db.TeamMatch, error)
Filip Kujawaf882e022022-12-14 13:14:08 -080085 ReturnAllNotes() ([]db.NotesData, error)
86 ReturnAllDriverRankings() ([]db.DriverRankingData, error)
Milo Lin1d59f0c2022-06-22 20:30:58 -070087 ReturnAllShifts() ([]db.Shift, error)
Emily Markova290147d2023-03-03 22:40:06 -080088 ReturnStats2023() ([]db.Stats2023, error)
Filip Kujawaf3f9def2023-04-20 13:46:46 -070089 ReturnStats2023ForTeam(teamNumber string, matchNumber int32, setNumber int32, compLevel string, preScouting bool) ([]db.Stats2023, error)
Milo Lin1d59f0c2022-06-22 20:30:58 -070090 QueryAllShifts(int) ([]db.Shift, error)
Emily Markovae68b7632023-12-30 14:17:55 -080091 QueryNotes(string) ([]string, error)
Emily Markovafaecfe12023-07-01 12:40:03 -070092 QueryPitImages(string) ([]db.RequestedPitImage, error)
Emily Markova8e39f452023-12-23 12:17:30 -080093 ReturnPitImages() ([]db.PitImage, error)
Filip Kujawaf947cb42022-11-21 10:00:30 -080094 AddNotes(db.NotesData) error
Emily Markovafaecfe12023-07-01 12:40:03 -070095 AddPitImage(db.PitImage) error
Filip Kujawa210a03b2022-11-24 14:41:11 -080096 AddDriverRanking(db.DriverRankingData) error
Sabina Leaver9b4eb312023-02-20 19:58:17 -080097 AddAction(db.Action) error
Filip Kujawac1ded372023-05-27 14:33:43 -070098 DeleteFromStats(string, int32, int32, string) error
99 DeleteFromActions(string, int32, int32, string) error
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800100}
101
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800102// Handles unknown requests. Just returns a 404.
103func unknown(w http.ResponseWriter, req *http.Request) {
104 w.WriteHeader(http.StatusNotFound)
105}
106
107func respondWithError(w http.ResponseWriter, statusCode int, errorMessage string) {
108 builder := flatbuffers.NewBuilder(1024)
109 builder.Finish((&error_response.ErrorResponseT{
110 ErrorMessage: errorMessage,
111 }).Pack(builder))
112 w.WriteHeader(statusCode)
113 w.Write(builder.FinishedBytes())
114}
115
116func respondNotImplemented(w http.ResponseWriter) {
117 respondWithError(w, http.StatusNotImplemented, "")
118}
119
Philipp Schraderb7e75932022-03-26 16:18:34 -0700120func 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 -0800121 success := true
122 defer func() {
123 if r := recover(); r != nil {
Philipp Schraderb7e75932022-03-26 16:18:34 -0700124 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse %s: %v", requestName, r))
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800125 success = false
126 }
127 }()
Philipp Schraderb7e75932022-03-26 16:18:34 -0700128 result := parser(buf, 0)
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800129 return result, success
130}
131
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700132// Parses the authorization information that the browser inserts into the
133// headers. The authorization follows this format:
134//
Philipp Schrader35bb1532023-03-05 13:49:12 -0800135// req.Headers["Authorization"] = []string{"Basic <base64 encoded username:password>"}
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700136func parseUsername(req *http.Request) string {
137 auth, ok := req.Header["Authorization"]
138 if !ok {
139 return "unknown"
140 }
141
142 parts := strings.Split(auth[0], " ")
143 if !(len(parts) == 2 && parts[0] == "Basic") {
144 return "unknown"
145 }
146
147 info, err := base64.StdEncoding.DecodeString(parts[1])
148 if err != nil {
149 log.Println("ERROR: Failed to parse Basic authentication.")
150 return "unknown"
151 }
152
153 loginParts := strings.Split(string(info), ":")
154 if len(loginParts) != 2 {
155 return "unknown"
156 }
157 return loginParts[0]
158}
159
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800160// Handles a RequestAllMaches request.
161type requestAllMatchesHandler struct {
162 db Database
163}
164
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800165// Change structure of match objects in the database(1 per team) to
166// the old match structure(1 per match) that the webserver uses.
167// We use the information in this struct to identify which match object
168// corresponds to which old match structure object.
169type MatchAssemblyKey struct {
170 MatchNumber int32
171 SetNumber int32
172 CompLevel string
173}
174
Emily Markovabf24c9e2023-02-08 20:31:11 -0800175func findIndexInList(list []string, comp_level string) (int, error) {
176 for index, value := range list {
177 if value == comp_level {
178 return index, nil
179 }
180 }
181 return -1, errors.New(fmt.Sprint("Failed to find comp level ", comp_level, " in list ", list))
182}
183
Emily Markovab8551572023-03-22 19:49:39 -0700184func (handler requestAllMatchesHandler) teamHasBeenDataScouted(key MatchAssemblyKey, teamNumber string) (bool, error) {
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800185 stats, err := handler.db.ReturnStats2023ForTeam(
Filip Kujawaf3f9def2023-04-20 13:46:46 -0700186 teamNumber, key.MatchNumber, key.SetNumber, key.CompLevel, false)
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800187 if err != nil {
188 return false, err
189 }
190 return (len(stats) > 0), nil
191}
192
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800193func (handler requestAllMatchesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
194 requestBytes, err := io.ReadAll(req.Body)
195 if err != nil {
196 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
197 return
198 }
199
Philipp Schraderb7e75932022-03-26 16:18:34 -0700200 _, success := parseRequest(w, requestBytes, "RequestAllMatches", request_all_matches.GetRootAsRequestAllMatches)
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800201 if !success {
202 return
203 }
204
205 matches, err := handler.db.ReturnMatches()
206 if err != nil {
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700207 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
Philipp Schrader2e7eb0002022-03-02 22:52:39 -0800208 return
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800209 }
210
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800211 assembledMatches := map[MatchAssemblyKey]request_all_matches_response.MatchT{}
Emily Markovabf24c9e2023-02-08 20:31:11 -0800212
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800213 for _, match := range matches {
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800214 key := MatchAssemblyKey{match.MatchNumber, match.SetNumber, match.CompLevel}
215
216 // Retrieve the converted match structure we have assembled so
217 // far. If we haven't started assembling one yet, then start a
218 // new one.
Emily Markovabf24c9e2023-02-08 20:31:11 -0800219 entry, ok := assembledMatches[key]
220 if !ok {
221 entry = request_all_matches_response.MatchT{
222 MatchNumber: match.MatchNumber,
223 SetNumber: match.SetNumber,
224 CompLevel: match.CompLevel,
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800225 DataScouted: &request_all_matches_response.ScoutedLevelT{},
Emily Markovabf24c9e2023-02-08 20:31:11 -0800226 }
227 }
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800228
Emily Markovab8551572023-03-22 19:49:39 -0700229 var team *string
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800230 var dataScoutedTeam *bool
231
232 // Fill in the field for the match that we have in in the
233 // database. In the database, each match row only has 1 team
234 // number.
Emily Markovabf24c9e2023-02-08 20:31:11 -0800235 switch match.Alliance {
236 case "R":
237 switch match.AlliancePosition {
238 case 1:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800239 team = &entry.R1
240 dataScoutedTeam = &entry.DataScouted.R1
Emily Markovabf24c9e2023-02-08 20:31:11 -0800241 case 2:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800242 team = &entry.R2
243 dataScoutedTeam = &entry.DataScouted.R2
Emily Markovabf24c9e2023-02-08 20:31:11 -0800244 case 3:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800245 team = &entry.R3
246 dataScoutedTeam = &entry.DataScouted.R3
Emily Markovabf24c9e2023-02-08 20:31:11 -0800247 default:
248 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown red position ", strconv.Itoa(int(match.AlliancePosition)), " in match ", strconv.Itoa(int(match.MatchNumber))))
249 return
250 }
251 case "B":
252 switch match.AlliancePosition {
253 case 1:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800254 team = &entry.B1
255 dataScoutedTeam = &entry.DataScouted.B1
Emily Markovabf24c9e2023-02-08 20:31:11 -0800256 case 2:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800257 team = &entry.B2
258 dataScoutedTeam = &entry.DataScouted.B2
Emily Markovabf24c9e2023-02-08 20:31:11 -0800259 case 3:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800260 team = &entry.B3
261 dataScoutedTeam = &entry.DataScouted.B3
Emily Markovabf24c9e2023-02-08 20:31:11 -0800262 default:
263 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown blue position ", strconv.Itoa(int(match.AlliancePosition)), " in match ", strconv.Itoa(int(match.MatchNumber))))
264 return
265 }
266 default:
267 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown alliance ", match.Alliance, " in match ", strconv.Itoa(int(match.AlliancePosition))))
268 return
269 }
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800270
271 *team = match.TeamNumber
272
273 // Figure out if this team has been data scouted already.
274 *dataScoutedTeam, err = handler.teamHasBeenDataScouted(key, match.TeamNumber)
275 if err != nil {
276 respondWithError(w, http.StatusInternalServerError, fmt.Sprint(
277 "Failed to determine data scouting status for team ",
278 strconv.Itoa(int(match.AlliancePosition)),
279 " in match ",
280 strconv.Itoa(int(match.MatchNumber)),
281 err))
282 return
283 }
284
Emily Markovabf24c9e2023-02-08 20:31:11 -0800285 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
Alex Perry81f96ba2022-03-13 18:26:19 -0700358type submitNoteScoutingHandler struct {
359 db Database
360}
361
362func (handler submitNoteScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
363 requestBytes, err := io.ReadAll(req.Body)
364 if err != nil {
365 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
366 return
367 }
368
Philipp Schraderb7e75932022-03-26 16:18:34 -0700369 request, success := parseRequest(w, requestBytes, "SubmitNotes", submit_notes.GetRootAsSubmitNotes)
Alex Perry81f96ba2022-03-13 18:26:19 -0700370 if !success {
371 return
372 }
373
Filip Kujawaf947cb42022-11-21 10:00:30 -0800374 err = handler.db.AddNotes(db.NotesData{
Emily Markovae68b7632023-12-30 14:17:55 -0800375 TeamNumber: string(request.Team()),
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800376 Notes: string(request.Notes()),
377 GoodDriving: bool(request.GoodDriving()),
378 BadDriving: bool(request.BadDriving()),
Filip Kujawa11dc4c92023-04-13 08:55:43 -0700379 SolidPlacing: bool(request.SolidPlacing()),
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800380 SketchyPlacing: bool(request.SketchyPlacing()),
381 GoodDefense: bool(request.GoodDefense()),
382 BadDefense: bool(request.BadDefense()),
383 EasilyDefended: bool(request.EasilyDefended()),
Filip Kujawaf947cb42022-11-21 10:00:30 -0800384 })
Alex Perry81f96ba2022-03-13 18:26:19 -0700385 if err != nil {
386 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert notes: %v", err))
387 return
388 }
389
390 var response SubmitNotesResponseT
391 builder := flatbuffers.NewBuilder(10)
392 builder.Finish((&response).Pack(builder))
393 w.Write(builder.FinishedBytes())
394}
395
Emily Markovafaecfe12023-07-01 12:40:03 -0700396type submitPitImageScoutingHandler struct {
397 db Database
398}
399
400func (handler submitPitImageScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
401 requestBytes, err := io.ReadAll(req.Body)
402 if err != nil {
403 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
404 return
405 }
406
407 request, success := parseRequest(w, requestBytes, "SubmitPitImage", submit_pit_image.GetRootAsSubmitPitImage)
408 if !success {
409 return
410 }
411
412 err = handler.db.AddPitImage(db.PitImage{
413 TeamNumber: string(request.TeamNumber()),
414 CheckSum: db.ComputeSha256FromByteArray(request.ImageDataBytes()),
415 ImagePath: string(request.ImagePath()),
416 ImageData: request.ImageDataBytes(),
417 })
418 if err != nil {
419 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert notes: %v", err))
420 return
421 }
422
423 var response SubmitPitImageResponseT
424 builder := flatbuffers.NewBuilder(10)
425 builder.Finish((&response).Pack(builder))
426 w.Write(builder.FinishedBytes())
427}
428
Emily Markova1abe9782023-03-11 19:45:38 -0800429func ConvertActionsToStat(submitActions *submit_actions.SubmitActions) (db.Stats2023, error) {
430 overall_time := int64(0)
431 cycles := int64(0)
432 picked_up := false
433 lastPlacedTime := int64(0)
Philipp Schrader4b489222023-04-15 16:40:16 -0700434 stat := db.Stats2023{
435 PreScouting: submitActions.PreScouting(),
436 TeamNumber: string(submitActions.TeamNumber()), MatchNumber: submitActions.MatchNumber(), SetNumber: submitActions.SetNumber(), CompLevel: string(submitActions.CompLevel()),
Emily Markova1abe9782023-03-11 19:45:38 -0800437 StartingQuadrant: 0, LowCubesAuto: 0, MiddleCubesAuto: 0, HighCubesAuto: 0, CubesDroppedAuto: 0,
438 LowConesAuto: 0, MiddleConesAuto: 0, HighConesAuto: 0, ConesDroppedAuto: 0, LowCubes: 0, MiddleCubes: 0, HighCubes: 0,
Philipp Schradere11114f2023-04-15 17:04:25 -0700439 CubesDropped: 0, LowCones: 0, MiddleCones: 0, HighCones: 0, ConesDropped: 0, SuperchargedPieces: 0, AvgCycle: 0, CollectedBy: "",
Emily Markova1abe9782023-03-11 19:45:38 -0800440 }
441 // Loop over all actions.
442 for i := 0; i < submitActions.ActionsListLength(); i++ {
443 var action submit_actions.Action
444 if !submitActions.ActionsList(&action, i) {
445 return db.Stats2023{}, errors.New(fmt.Sprintf("Failed to parse submit_actions.Action"))
446 }
447 actionTable := new(flatbuffers.Table)
448 action_type := action.ActionTakenType()
449 if !action.ActionTaken(actionTable) {
450 return db.Stats2023{}, errors.New(fmt.Sprint("Failed to parse sub-action or sub-action was missing"))
451 }
452 if action_type == submit_actions.ActionTypeStartMatchAction {
453 var startMatchAction submit_actions.StartMatchAction
454 startMatchAction.Init(actionTable.Bytes, actionTable.Pos)
455 stat.StartingQuadrant = startMatchAction.Position()
Filip Kujawa0b4b1e52023-04-15 14:05:40 -0700456 } else if action_type == submit_actions.ActionTypeMobilityAction {
457 var mobilityAction submit_actions.MobilityAction
458 mobilityAction.Init(actionTable.Bytes, actionTable.Pos)
459 if mobilityAction.Mobility() {
460 stat.Mobility = true
461 }
462
Emily Markova46a69bf2023-03-22 20:45:52 -0700463 } else if action_type == submit_actions.ActionTypeAutoBalanceAction {
464 var autoBalanceAction submit_actions.AutoBalanceAction
465 autoBalanceAction.Init(actionTable.Bytes, actionTable.Pos)
466 if autoBalanceAction.Docked() {
467 stat.DockedAuto = true
468 }
469 if autoBalanceAction.Engaged() {
470 stat.EngagedAuto = true
471 }
Emily Markova63c63f62023-03-29 20:57:35 -0700472 if autoBalanceAction.BalanceAttempt() {
473 stat.BalanceAttemptAuto = true
474 }
Emily Markova1abe9782023-03-11 19:45:38 -0800475 } else if action_type == submit_actions.ActionTypePickupObjectAction {
476 var pick_up_action submit_actions.PickupObjectAction
477 pick_up_action.Init(actionTable.Bytes, actionTable.Pos)
478 if picked_up == true {
479 object := pick_up_action.ObjectType().String()
480 auto := pick_up_action.Auto()
481 if object == "kCube" && auto == false {
482 stat.CubesDropped += 1
483 } else if object == "kCube" && auto == true {
484 stat.CubesDroppedAuto += 1
485 } else if object == "kCone" && auto == false {
486 stat.ConesDropped += 1
487 } else if object == "kCube" && auto == true {
488 stat.ConesDroppedAuto += 1
489 }
490 } else {
491 picked_up = true
492 }
493 } else if action_type == submit_actions.ActionTypePlaceObjectAction {
494 var place_action submit_actions.PlaceObjectAction
495 place_action.Init(actionTable.Bytes, actionTable.Pos)
496 if !picked_up {
497 return db.Stats2023{}, errors.New(fmt.Sprintf("Got PlaceObjectAction without corresponding PickupObjectAction"))
498 }
499 object := place_action.ObjectType()
500 level := place_action.ScoreLevel()
501 auto := place_action.Auto()
502 if object == 0 && level == 0 && auto == true {
503 stat.LowCubesAuto += 1
504 } else if object == 0 && level == 0 && auto == false {
505 stat.LowCubes += 1
506 } else if object == 0 && level == 1 && auto == true {
507 stat.MiddleCubesAuto += 1
508 } else if object == 0 && level == 1 && auto == false {
509 stat.MiddleCubes += 1
510 } else if object == 0 && level == 2 && auto == true {
511 stat.HighCubesAuto += 1
512 } else if object == 0 && level == 2 && auto == false {
513 stat.HighCubes += 1
514 } else if object == 1 && level == 0 && auto == true {
515 stat.LowConesAuto += 1
516 } else if object == 1 && level == 0 && auto == false {
517 stat.LowCones += 1
518 } else if object == 1 && level == 1 && auto == true {
519 stat.MiddleConesAuto += 1
520 } else if object == 1 && level == 1 && auto == false {
521 stat.MiddleCones += 1
522 } else if object == 1 && level == 2 && auto == true {
523 stat.HighConesAuto += 1
524 } else if object == 1 && level == 2 && auto == false {
525 stat.HighCones += 1
Filip Kujawa7a045e72023-04-13 08:41:09 -0700526 } else if level == 3 {
527 stat.SuperchargedPieces += 1
Emily Markova1abe9782023-03-11 19:45:38 -0800528 } else {
529 return db.Stats2023{}, errors.New(fmt.Sprintf("Got unknown ObjectType/ScoreLevel/Auto combination"))
530 }
531 picked_up = false
532 if lastPlacedTime != int64(0) {
533 // If this is not the first time we place,
534 // start counting cycle time. We define cycle
535 // time as the time between placements.
536 overall_time += int64(action.Timestamp()) - lastPlacedTime
537 cycles += 1
538 }
539 lastPlacedTime = int64(action.Timestamp())
Emily Markova46a69bf2023-03-22 20:45:52 -0700540 } else if action_type == submit_actions.ActionTypeEndMatchAction {
541 var endMatchAction submit_actions.EndMatchAction
542 endMatchAction.Init(actionTable.Bytes, actionTable.Pos)
543 if endMatchAction.Docked() {
544 stat.Docked = true
545 }
546 if endMatchAction.Engaged() {
547 stat.Engaged = true
548 }
Emily Markova63c63f62023-03-29 20:57:35 -0700549 if endMatchAction.BalanceAttempt() {
550 stat.BalanceAttempt = true
551 }
Emily Markova1abe9782023-03-11 19:45:38 -0800552 }
553 }
554 if cycles != 0 {
Philipp Schrader8c878a22023-03-20 22:36:38 -0700555 stat.AvgCycle = overall_time / cycles
Emily Markova1abe9782023-03-11 19:45:38 -0800556 } else {
557 stat.AvgCycle = 0
558 }
559 return stat, nil
560}
561
Emily Markova290147d2023-03-03 22:40:06 -0800562// Handles a Request2023DataScouting request.
563type request2023DataScoutingHandler struct {
564 db Database
565}
566
567func (handler request2023DataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
568 requestBytes, err := io.ReadAll(req.Body)
569 if err != nil {
570 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
571 return
572 }
573
574 _, success := parseRequest(w, requestBytes, "Request2023DataScouting", request_2023_data_scouting.GetRootAsRequest2023DataScouting)
575 if !success {
576 return
577 }
578
579 stats, err := handler.db.ReturnStats2023()
580 if err != nil {
581 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
582 return
583 }
584
585 var response Request2023DataScoutingResponseT
586 for _, stat := range stats {
587 response.StatsList = append(response.StatsList, &request_2023_data_scouting_response.Stats2023T{
Emily Markova63c63f62023-03-29 20:57:35 -0700588 TeamNumber: stat.TeamNumber,
589 MatchNumber: stat.MatchNumber,
590 SetNumber: stat.SetNumber,
591 CompLevel: stat.CompLevel,
592 StartingQuadrant: stat.StartingQuadrant,
593 LowCubesAuto: stat.LowCubesAuto,
594 MiddleCubesAuto: stat.MiddleCubesAuto,
595 HighCubesAuto: stat.HighCubesAuto,
596 CubesDroppedAuto: stat.CubesDroppedAuto,
597 LowConesAuto: stat.LowConesAuto,
598 MiddleConesAuto: stat.MiddleConesAuto,
599 HighConesAuto: stat.HighConesAuto,
600 ConesDroppedAuto: stat.ConesDroppedAuto,
601 LowCubes: stat.LowCubes,
602 MiddleCubes: stat.MiddleCubes,
603 HighCubes: stat.HighCubes,
604 CubesDropped: stat.CubesDropped,
605 LowCones: stat.LowCones,
606 MiddleCones: stat.MiddleCones,
607 HighCones: stat.HighCones,
608 ConesDropped: stat.ConesDropped,
Filip Kujawa7a045e72023-04-13 08:41:09 -0700609 SuperchargedPieces: stat.SuperchargedPieces,
Emily Markova63c63f62023-03-29 20:57:35 -0700610 AvgCycle: stat.AvgCycle,
Filip Kujawa0b4b1e52023-04-15 14:05:40 -0700611 Mobility: stat.Mobility,
Emily Markova63c63f62023-03-29 20:57:35 -0700612 DockedAuto: stat.DockedAuto,
613 EngagedAuto: stat.EngagedAuto,
614 BalanceAttemptAuto: stat.BalanceAttemptAuto,
615 Docked: stat.Docked,
616 Engaged: stat.Engaged,
617 BalanceAttempt: stat.BalanceAttempt,
618 CollectedBy: stat.CollectedBy,
Emily Markova290147d2023-03-03 22:40:06 -0800619 })
620 }
621
622 builder := flatbuffers.NewBuilder(50 * 1024)
623 builder.Finish((&response).Pack(builder))
624 w.Write(builder.FinishedBytes())
625}
626
Emily Markova8e39f452023-12-23 12:17:30 -0800627type requestAllPitImagesHandler struct {
628 db Database
629}
630
631func (handler requestAllPitImagesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
632 requestBytes, err := io.ReadAll(req.Body)
633 if err != nil {
634 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
635 return
636 }
637
638 _, success := parseRequest(w, requestBytes, "RequestAllPitImages", request_all_pit_images.GetRootAsRequestAllPitImages)
639 if !success {
640 return
641 }
642
643 images, err := handler.db.ReturnPitImages()
644 if err != nil {
645 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to get pit images: %v", err))
646 return
647 }
648
649 var response RequestAllPitImagesResponseT
650 for _, data := range images {
651 response.PitImageList = append(response.PitImageList, &request_all_pit_images_response.PitImageT{
652 TeamNumber: data.TeamNumber,
653 ImagePath: data.ImagePath,
654 CheckSum: data.CheckSum,
655 })
656 }
657
658 builder := flatbuffers.NewBuilder(1024)
659 builder.Finish((&response).Pack(builder))
660 w.Write(builder.FinishedBytes())
661}
662
Emily Markovafaecfe12023-07-01 12:40:03 -0700663type requestPitImagesHandler struct {
664 db Database
665}
666
667func (handler requestPitImagesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
668 requestBytes, err := io.ReadAll(req.Body)
669 if err != nil {
670 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
671 return
672 }
673
674 request, success := parseRequest(w, requestBytes, "RequestPitImages", request_pit_images.GetRootAsRequestPitImages)
675 if !success {
676 return
677 }
678
679 images, err := handler.db.QueryPitImages(string(request.TeamNumber()))
680 if err != nil {
681 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query pit images: %v", err))
682 return
683 }
684
685 var response RequestPitImagesResponseT
686 for _, data := range images {
687 response.PitImageList = append(response.PitImageList, &request_pit_images_response.PitImageT{
688 TeamNumber: data.TeamNumber,
689 ImagePath: data.ImagePath,
690 CheckSum: data.CheckSum,
691 })
692 }
693
694 builder := flatbuffers.NewBuilder(1024)
695 builder.Finish((&response).Pack(builder))
696 w.Write(builder.FinishedBytes())
697}
698
Alex Perry81f96ba2022-03-13 18:26:19 -0700699type requestNotesForTeamHandler struct {
700 db Database
701}
702
703func (handler requestNotesForTeamHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
704 requestBytes, err := io.ReadAll(req.Body)
705 if err != nil {
706 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
707 return
708 }
709
Philipp Schraderb7e75932022-03-26 16:18:34 -0700710 request, success := parseRequest(w, requestBytes, "RequestNotesForTeam", request_notes_for_team.GetRootAsRequestNotesForTeam)
Alex Perry81f96ba2022-03-13 18:26:19 -0700711 if !success {
712 return
713 }
714
Emily Markovae68b7632023-12-30 14:17:55 -0800715 notes, err := handler.db.QueryNotes(string(request.Team()))
Alex Perry81f96ba2022-03-13 18:26:19 -0700716 if err != nil {
717 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query notes: %v", err))
718 return
719 }
720
721 var response RequestNotesForTeamResponseT
Philipp Schradereecb8962022-06-01 21:02:42 -0700722 for _, data := range notes {
Alex Perry81f96ba2022-03-13 18:26:19 -0700723 response.Notes = append(response.Notes, &request_notes_for_team_response.NoteT{data})
724 }
725
726 builder := flatbuffers.NewBuilder(1024)
727 builder.Finish((&response).Pack(builder))
728 w.Write(builder.FinishedBytes())
729}
730
Milo Lin1d59f0c2022-06-22 20:30:58 -0700731type requestShiftScheduleHandler struct {
732 db Database
733}
734
735func (handler requestShiftScheduleHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
736 requestBytes, err := io.ReadAll(req.Body)
737 if err != nil {
738 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
739 return
740 }
741
742 _, success := parseRequest(w, requestBytes, "RequestShiftSchedule", request_shift_schedule.GetRootAsRequestShiftSchedule)
743 if !success {
744 return
745 }
746
747 shiftData, err := handler.db.ReturnAllShifts()
748 if err != nil {
749 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query shift schedule: %v", err))
750 return
751 }
752
753 var response RequestShiftScheduleResponseT
754 for _, shifts := range shiftData {
755 response.ShiftSchedule = append(response.ShiftSchedule, &request_shift_schedule_response.MatchAssignmentT{
756 MatchNumber: shifts.MatchNumber,
Philipp Schrader2ff455b2023-05-03 22:11:50 -0700757 R1Scouter: shifts.R1scouter,
758 R2Scouter: shifts.R2scouter,
759 R3Scouter: shifts.R3scouter,
760 B1Scouter: shifts.B1scouter,
761 B2Scouter: shifts.B2scouter,
762 B3Scouter: shifts.B3scouter,
Milo Lin1d59f0c2022-06-22 20:30:58 -0700763 })
764 }
765
766 builder := flatbuffers.NewBuilder(1024)
767 builder.Finish((&response).Pack(builder))
768 w.Write(builder.FinishedBytes())
769}
770
771type submitShiftScheduleHandler struct {
772 db Database
773}
774
775func (handler submitShiftScheduleHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
776 // Get the username of the person submitting the data.
777 username := parseUsername(req)
778
779 requestBytes, err := io.ReadAll(req.Body)
780 if err != nil {
781 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
782 return
783 }
784
785 request, success := parseRequest[SubmitShiftSchedule](w, requestBytes, "SubmitShiftSchedule", submit_shift_schedule.GetRootAsSubmitShiftSchedule)
786 if !success {
787 return
788 }
789
790 log.Println("Got shift schedule from", username)
791 shift_schedule_length := request.ShiftScheduleLength()
792 for i := 0; i < shift_schedule_length; i++ {
793 var match_assignment submit_shift_schedule.MatchAssignment
794 request.ShiftSchedule(&match_assignment, i)
795 current_shift := db.Shift{
796 MatchNumber: match_assignment.MatchNumber(),
Philipp Schrader2ff455b2023-05-03 22:11:50 -0700797 R1scouter: string(match_assignment.R1Scouter()),
798 R2scouter: string(match_assignment.R2Scouter()),
799 R3scouter: string(match_assignment.R3Scouter()),
800 B1scouter: string(match_assignment.B1Scouter()),
801 B2scouter: string(match_assignment.B2Scouter()),
802 B3scouter: string(match_assignment.B3Scouter()),
Milo Lin1d59f0c2022-06-22 20:30:58 -0700803 }
804 err = handler.db.AddToShift(current_shift)
805 if err != nil {
806 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to submit shift schedule: ", err))
807 return
808 }
809 }
810
811 builder := flatbuffers.NewBuilder(50 * 1024)
812 builder.Finish((&SubmitShiftScheduleResponseT{}).Pack(builder))
813 w.Write(builder.FinishedBytes())
814}
815
Filip Kujawa210a03b2022-11-24 14:41:11 -0800816type SubmitDriverRankingHandler struct {
817 db Database
818}
819
820func (handler SubmitDriverRankingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
821 requestBytes, err := io.ReadAll(req.Body)
822 if err != nil {
823 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
824 return
825 }
826
827 request, success := parseRequest(w, requestBytes, "SubmitDriverRanking", submit_driver_ranking.GetRootAsSubmitDriverRanking)
828 if !success {
829 return
830 }
831
832 err = handler.db.AddDriverRanking(db.DriverRankingData{
833 MatchNumber: request.MatchNumber(),
Emily Markovae68b7632023-12-30 14:17:55 -0800834 Rank1: string(request.Rank1()),
835 Rank2: string(request.Rank2()),
836 Rank3: string(request.Rank3()),
Filip Kujawa210a03b2022-11-24 14:41:11 -0800837 })
838
839 if err != nil {
840 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert driver ranking: %v", err))
841 return
842 }
843
844 var response SubmitDriverRankingResponseT
845 builder := flatbuffers.NewBuilder(10)
846 builder.Finish((&response).Pack(builder))
847 w.Write(builder.FinishedBytes())
848}
849
Filip Kujawaf882e022022-12-14 13:14:08 -0800850type requestAllNotesHandler struct {
851 db Database
852}
853
854func (handler requestAllNotesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
855 requestBytes, err := io.ReadAll(req.Body)
856 if err != nil {
857 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
858 return
859 }
860
861 _, success := parseRequest(w, requestBytes, "RequestAllNotes", request_all_notes.GetRootAsRequestAllNotes)
862 if !success {
863 return
864 }
865
866 notes, err := handler.db.ReturnAllNotes()
867 if err != nil {
868 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
869 return
870 }
871
872 var response RequestAllNotesResponseT
873 for _, note := range notes {
874 response.NoteList = append(response.NoteList, &request_all_notes_response.NoteT{
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800875 Team: note.TeamNumber,
876 Notes: note.Notes,
877 GoodDriving: note.GoodDriving,
878 BadDriving: note.BadDriving,
Filip Kujawa11dc4c92023-04-13 08:55:43 -0700879 SolidPlacing: note.SolidPlacing,
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800880 SketchyPlacing: note.SketchyPlacing,
881 GoodDefense: note.GoodDefense,
882 BadDefense: note.BadDefense,
883 EasilyDefended: note.EasilyDefended,
Filip Kujawaf882e022022-12-14 13:14:08 -0800884 })
885 }
886
887 builder := flatbuffers.NewBuilder(50 * 1024)
888 builder.Finish((&response).Pack(builder))
889 w.Write(builder.FinishedBytes())
890}
891
892type requestAllDriverRankingsHandler struct {
893 db Database
894}
895
896func (handler requestAllDriverRankingsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
897 requestBytes, err := io.ReadAll(req.Body)
898 if err != nil {
899 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
900 return
901 }
902
903 _, success := parseRequest(w, requestBytes, "RequestAllDriverRankings", request_all_driver_rankings.GetRootAsRequestAllDriverRankings)
904 if !success {
905 return
906 }
907
908 rankings, err := handler.db.ReturnAllDriverRankings()
909 if err != nil {
910 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
911 return
912 }
913
914 var response RequestAllDriverRankingsResponseT
915 for _, ranking := range rankings {
916 response.DriverRankingList = append(response.DriverRankingList, &request_all_driver_rankings_response.RankingT{
917 MatchNumber: ranking.MatchNumber,
918 Rank1: ranking.Rank1,
919 Rank2: ranking.Rank2,
920 Rank3: ranking.Rank3,
921 })
922 }
923
924 builder := flatbuffers.NewBuilder(50 * 1024)
925 builder.Finish((&response).Pack(builder))
926 w.Write(builder.FinishedBytes())
927}
928
Sabina Leaver9b4eb312023-02-20 19:58:17 -0800929type submitActionsHandler struct {
930 db Database
931}
932
933func (handler submitActionsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
934 // Get the username of the person submitting the data.
935 username := parseUsername(req)
936
937 requestBytes, err := io.ReadAll(req.Body)
938 if err != nil {
939 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
940 return
941 }
942
943 request, success := parseRequest(w, requestBytes, "SubmitActions", submit_actions.GetRootAsSubmitActions)
944 if !success {
945 return
946 }
947
948 log.Println("Got actions for match", request.MatchNumber(), "team", request.TeamNumber(), "from", username)
949
950 for i := 0; i < request.ActionsListLength(); i++ {
951
952 var action Action
953 request.ActionsList(&action, i)
954
955 dbAction := db.Action{
Philipp Schrader4b489222023-04-15 16:40:16 -0700956 PreScouting: request.PreScouting(),
Sabina Leaver9b4eb312023-02-20 19:58:17 -0800957 TeamNumber: string(request.TeamNumber()),
958 MatchNumber: request.MatchNumber(),
959 SetNumber: request.SetNumber(),
960 CompLevel: string(request.CompLevel()),
961 //TODO: Serialize CompletedAction
962 CompletedAction: []byte{},
Philipp Schrader670a1c82023-05-17 19:42:43 -0700963 Timestamp: action.Timestamp(),
Sabina Leaver9b4eb312023-02-20 19:58:17 -0800964 CollectedBy: username,
965 }
966
967 // Do some error checking.
968 if action.Timestamp() < 0 {
969 respondWithError(w, http.StatusBadRequest, fmt.Sprint(
970 "Invalid timestamp field value of ", action.Timestamp()))
971 return
972 }
973
974 err = handler.db.AddAction(dbAction)
975 if err != nil {
976 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to add action to database: ", err))
977 return
978 }
979 }
980
Philipp Schradere11114f2023-04-15 17:04:25 -0700981 stats, err := ConvertActionsToStat(request)
982 if err != nil {
983 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to convert actions to stats: ", err))
984 return
985 }
986
987 stats.CollectedBy = username
988
989 err = handler.db.AddToStats2023(stats)
990 if err != nil {
991 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to submit stats: ", stats, ": ", err))
992 return
993 }
994
Sabina Leaver9b4eb312023-02-20 19:58:17 -0800995 builder := flatbuffers.NewBuilder(50 * 1024)
996 builder.Finish((&SubmitActionsResponseT{}).Pack(builder))
997 w.Write(builder.FinishedBytes())
998}
999
Filip Kujawac1ded372023-05-27 14:33:43 -07001000type Delete2023DataScoutingHandler struct {
1001 db Database
1002}
1003
1004func (handler Delete2023DataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
1005 requestBytes, err := io.ReadAll(req.Body)
1006 if err != nil {
1007 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
1008 return
1009 }
1010
1011 request, success := parseRequest(w, requestBytes, "Delete2023DataScouting", delete_2023_data_scouting.GetRootAsDelete2023DataScouting)
1012 if !success {
1013 return
1014 }
1015
1016 err = handler.db.DeleteFromStats(
1017 string(request.CompLevel()),
1018 request.MatchNumber(),
1019 request.SetNumber(),
1020 string(request.TeamNumber()))
1021
1022 if err != nil {
1023 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to delete from stats: %v", err))
1024 return
1025 }
1026
1027 err = handler.db.DeleteFromActions(
1028 string(request.CompLevel()),
1029 request.MatchNumber(),
1030 request.SetNumber(),
1031 string(request.TeamNumber()))
1032
1033 if err != nil {
1034 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to delete from actions: %v", err))
1035 return
1036 }
1037
1038 var response Delete2023DataScoutingResponseT
1039 builder := flatbuffers.NewBuilder(10)
1040 builder.Finish((&response).Pack(builder))
1041 w.Write(builder.FinishedBytes())
1042}
1043
Philipp Schrader43c730b2023-02-26 20:27:44 -08001044func HandleRequests(db Database, scoutingServer server.ScoutingServer) {
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08001045 scoutingServer.HandleFunc("/requests", unknown)
Philipp Schradercbf5c6a2022-02-27 23:25:19 -08001046 scoutingServer.Handle("/requests/request/all_matches", requestAllMatchesHandler{db})
Filip Kujawaf882e022022-12-14 13:14:08 -08001047 scoutingServer.Handle("/requests/request/all_notes", requestAllNotesHandler{db})
1048 scoutingServer.Handle("/requests/request/all_driver_rankings", requestAllDriverRankingsHandler{db})
Emily Markova290147d2023-03-03 22:40:06 -08001049 scoutingServer.Handle("/requests/request/2023_data_scouting", request2023DataScoutingHandler{db})
Alex Perry81f96ba2022-03-13 18:26:19 -07001050 scoutingServer.Handle("/requests/submit/submit_notes", submitNoteScoutingHandler{db})
Emily Markovafaecfe12023-07-01 12:40:03 -07001051 scoutingServer.Handle("/requests/submit/submit_pit_image", submitPitImageScoutingHandler{db})
1052 scoutingServer.Handle("/requests/request/pit_images", requestPitImagesHandler{db})
Emily Markova8e39f452023-12-23 12:17:30 -08001053 scoutingServer.Handle("/requests/request/all_pit_images", requestAllPitImagesHandler{db})
Alex Perry81f96ba2022-03-13 18:26:19 -07001054 scoutingServer.Handle("/requests/request/notes_for_team", requestNotesForTeamHandler{db})
Milo Lin1d59f0c2022-06-22 20:30:58 -07001055 scoutingServer.Handle("/requests/submit/shift_schedule", submitShiftScheduleHandler{db})
1056 scoutingServer.Handle("/requests/request/shift_schedule", requestShiftScheduleHandler{db})
Filip Kujawa210a03b2022-11-24 14:41:11 -08001057 scoutingServer.Handle("/requests/submit/submit_driver_ranking", SubmitDriverRankingHandler{db})
Sabina Leaver9b4eb312023-02-20 19:58:17 -08001058 scoutingServer.Handle("/requests/submit/submit_actions", submitActionsHandler{db})
Filip Kujawac1ded372023-05-27 14:33:43 -07001059 scoutingServer.Handle("/requests/delete/delete_2023_data_scouting", Delete2023DataScoutingHandler{db})
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08001060}