blob: 51612ff89165191171adbc48e29176cf51bd01bc [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"
Alex Perry81f96ba2022-03-13 18:26:19 -070026 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team"
27 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team_response"
Emily Markovafaecfe12023-07-01 12:40:03 -070028 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_pit_images"
29 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_pit_images_response"
Milo Lin1d59f0c2022-06-22 20:30:58 -070030 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_shift_schedule"
31 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_shift_schedule_response"
Sabina Leaver759090b2023-01-14 20:42:56 -080032 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_actions"
33 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_actions_response"
Filip Kujawa210a03b2022-11-24 14:41:11 -080034 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_driver_ranking"
35 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_driver_ranking_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070036 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes"
37 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes_response"
Emily Markovafaecfe12023-07-01 12:40:03 -070038 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_pit_image"
39 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_pit_image_response"
Milo Lin1d59f0c2022-06-22 20:30:58 -070040 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_shift_schedule"
41 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_shift_schedule_response"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080042 "github.com/frc971/971-Robot-Code/scouting/webserver/server"
43 flatbuffers "github.com/google/flatbuffers/go"
44)
45
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080046type RequestAllMatches = request_all_matches.RequestAllMatches
47type RequestAllMatchesResponseT = request_all_matches_response.RequestAllMatchesResponseT
Filip Kujawaf882e022022-12-14 13:14:08 -080048type RequestAllDriverRankings = request_all_driver_rankings.RequestAllDriverRankings
49type RequestAllDriverRankingsResponseT = request_all_driver_rankings_response.RequestAllDriverRankingsResponseT
50type RequestAllNotes = request_all_notes.RequestAllNotes
51type RequestAllNotesResponseT = request_all_notes_response.RequestAllNotesResponseT
Emily Markova290147d2023-03-03 22:40:06 -080052type Request2023DataScouting = request_2023_data_scouting.Request2023DataScouting
53type Request2023DataScoutingResponseT = request_2023_data_scouting_response.Request2023DataScoutingResponseT
Alex Perry81f96ba2022-03-13 18:26:19 -070054type SubmitNotes = submit_notes.SubmitNotes
55type SubmitNotesResponseT = submit_notes_response.SubmitNotesResponseT
Emily Markovafaecfe12023-07-01 12:40:03 -070056type SubmitPitImage = submit_pit_image.SubmitPitImage
57type SubmitPitImageResponseT = submit_pit_image_response.SubmitPitImageResponseT
58type RequestPitImages = request_pit_images.RequestPitImages
59type RequestPitImagesResponseT = request_pit_images_response.RequestPitImagesResponseT
Alex Perry81f96ba2022-03-13 18:26:19 -070060type RequestNotesForTeam = request_notes_for_team.RequestNotesForTeam
61type RequestNotesForTeamResponseT = request_notes_for_team_response.RequestNotesForTeamResponseT
Milo Lin1d59f0c2022-06-22 20:30:58 -070062type RequestShiftSchedule = request_shift_schedule.RequestShiftSchedule
63type RequestShiftScheduleResponseT = request_shift_schedule_response.RequestShiftScheduleResponseT
64type SubmitShiftSchedule = submit_shift_schedule.SubmitShiftSchedule
65type SubmitShiftScheduleResponseT = submit_shift_schedule_response.SubmitShiftScheduleResponseT
Filip Kujawa210a03b2022-11-24 14:41:11 -080066type SubmitDriverRanking = submit_driver_ranking.SubmitDriverRanking
67type SubmitDriverRankingResponseT = submit_driver_ranking_response.SubmitDriverRankingResponseT
Sabina Leaver759090b2023-01-14 20:42:56 -080068type SubmitActions = submit_actions.SubmitActions
Sabina Leaver9b4eb312023-02-20 19:58:17 -080069type Action = submit_actions.Action
Sabina Leaver759090b2023-01-14 20:42:56 -080070type SubmitActionsResponseT = submit_actions_response.SubmitActionsResponseT
Filip Kujawac1ded372023-05-27 14:33:43 -070071type Delete2023DataScouting = delete_2023_data_scouting.Delete2023DataScouting
72type Delete2023DataScoutingResponseT = delete_2023_data_scouting_response.Delete2023DataScoutingResponseT
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080073
Philipp Schrader8747f1b2022-02-23 23:56:22 -080074// The interface we expect the database abstraction to conform to.
75// We use an interface here because it makes unit testing easier.
76type Database interface {
Emily Markovabf24c9e2023-02-08 20:31:11 -080077 AddToMatch(db.TeamMatch) error
Milo Lin1d59f0c2022-06-22 20:30:58 -070078 AddToShift(db.Shift) error
Emily Markova290147d2023-03-03 22:40:06 -080079 AddToStats2023(db.Stats2023) error
Emily Markovabf24c9e2023-02-08 20:31:11 -080080 ReturnMatches() ([]db.TeamMatch, error)
Filip Kujawaf882e022022-12-14 13:14:08 -080081 ReturnAllNotes() ([]db.NotesData, error)
82 ReturnAllDriverRankings() ([]db.DriverRankingData, error)
Milo Lin1d59f0c2022-06-22 20:30:58 -070083 ReturnAllShifts() ([]db.Shift, error)
Emily Markova290147d2023-03-03 22:40:06 -080084 ReturnStats2023() ([]db.Stats2023, error)
Filip Kujawaf3f9def2023-04-20 13:46:46 -070085 ReturnStats2023ForTeam(teamNumber string, matchNumber int32, setNumber int32, compLevel string, preScouting bool) ([]db.Stats2023, error)
Milo Lin1d59f0c2022-06-22 20:30:58 -070086 QueryAllShifts(int) ([]db.Shift, error)
Philipp Schradereecb8962022-06-01 21:02:42 -070087 QueryNotes(int32) ([]string, error)
Emily Markovafaecfe12023-07-01 12:40:03 -070088 QueryPitImages(string) ([]db.RequestedPitImage, error)
Filip Kujawaf947cb42022-11-21 10:00:30 -080089 AddNotes(db.NotesData) error
Emily Markovafaecfe12023-07-01 12:40:03 -070090 AddPitImage(db.PitImage) error
Filip Kujawa210a03b2022-11-24 14:41:11 -080091 AddDriverRanking(db.DriverRankingData) error
Sabina Leaver9b4eb312023-02-20 19:58:17 -080092 AddAction(db.Action) error
Filip Kujawac1ded372023-05-27 14:33:43 -070093 DeleteFromStats(string, int32, int32, string) error
94 DeleteFromActions(string, int32, int32, string) error
Philipp Schrader8747f1b2022-02-23 23:56:22 -080095}
96
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080097// Handles unknown requests. Just returns a 404.
98func unknown(w http.ResponseWriter, req *http.Request) {
99 w.WriteHeader(http.StatusNotFound)
100}
101
102func respondWithError(w http.ResponseWriter, statusCode int, errorMessage string) {
103 builder := flatbuffers.NewBuilder(1024)
104 builder.Finish((&error_response.ErrorResponseT{
105 ErrorMessage: errorMessage,
106 }).Pack(builder))
107 w.WriteHeader(statusCode)
108 w.Write(builder.FinishedBytes())
109}
110
111func respondNotImplemented(w http.ResponseWriter) {
112 respondWithError(w, http.StatusNotImplemented, "")
113}
114
Philipp Schraderb7e75932022-03-26 16:18:34 -0700115func 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 -0800116 success := true
117 defer func() {
118 if r := recover(); r != nil {
Philipp Schraderb7e75932022-03-26 16:18:34 -0700119 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse %s: %v", requestName, r))
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800120 success = false
121 }
122 }()
Philipp Schraderb7e75932022-03-26 16:18:34 -0700123 result := parser(buf, 0)
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800124 return result, success
125}
126
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700127// Parses the authorization information that the browser inserts into the
128// headers. The authorization follows this format:
129//
Philipp Schrader35bb1532023-03-05 13:49:12 -0800130// req.Headers["Authorization"] = []string{"Basic <base64 encoded username:password>"}
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700131func parseUsername(req *http.Request) string {
132 auth, ok := req.Header["Authorization"]
133 if !ok {
134 return "unknown"
135 }
136
137 parts := strings.Split(auth[0], " ")
138 if !(len(parts) == 2 && parts[0] == "Basic") {
139 return "unknown"
140 }
141
142 info, err := base64.StdEncoding.DecodeString(parts[1])
143 if err != nil {
144 log.Println("ERROR: Failed to parse Basic authentication.")
145 return "unknown"
146 }
147
148 loginParts := strings.Split(string(info), ":")
149 if len(loginParts) != 2 {
150 return "unknown"
151 }
152 return loginParts[0]
153}
154
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800155// Handles a RequestAllMaches request.
156type requestAllMatchesHandler struct {
157 db Database
158}
159
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800160// Change structure of match objects in the database(1 per team) to
161// the old match structure(1 per match) that the webserver uses.
162// We use the information in this struct to identify which match object
163// corresponds to which old match structure object.
164type MatchAssemblyKey struct {
165 MatchNumber int32
166 SetNumber int32
167 CompLevel string
168}
169
Emily Markovabf24c9e2023-02-08 20:31:11 -0800170func findIndexInList(list []string, comp_level string) (int, error) {
171 for index, value := range list {
172 if value == comp_level {
173 return index, nil
174 }
175 }
176 return -1, errors.New(fmt.Sprint("Failed to find comp level ", comp_level, " in list ", list))
177}
178
Emily Markovab8551572023-03-22 19:49:39 -0700179func (handler requestAllMatchesHandler) teamHasBeenDataScouted(key MatchAssemblyKey, teamNumber string) (bool, error) {
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800180 stats, err := handler.db.ReturnStats2023ForTeam(
Filip Kujawaf3f9def2023-04-20 13:46:46 -0700181 teamNumber, key.MatchNumber, key.SetNumber, key.CompLevel, false)
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800182 if err != nil {
183 return false, err
184 }
185 return (len(stats) > 0), nil
186}
187
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800188func (handler requestAllMatchesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
189 requestBytes, err := io.ReadAll(req.Body)
190 if err != nil {
191 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
192 return
193 }
194
Philipp Schraderb7e75932022-03-26 16:18:34 -0700195 _, success := parseRequest(w, requestBytes, "RequestAllMatches", request_all_matches.GetRootAsRequestAllMatches)
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800196 if !success {
197 return
198 }
199
200 matches, err := handler.db.ReturnMatches()
201 if err != nil {
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700202 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
Philipp Schrader2e7eb0002022-03-02 22:52:39 -0800203 return
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800204 }
205
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800206 assembledMatches := map[MatchAssemblyKey]request_all_matches_response.MatchT{}
Emily Markovabf24c9e2023-02-08 20:31:11 -0800207
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800208 for _, match := range matches {
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800209 key := MatchAssemblyKey{match.MatchNumber, match.SetNumber, match.CompLevel}
210
211 // Retrieve the converted match structure we have assembled so
212 // far. If we haven't started assembling one yet, then start a
213 // new one.
Emily Markovabf24c9e2023-02-08 20:31:11 -0800214 entry, ok := assembledMatches[key]
215 if !ok {
216 entry = request_all_matches_response.MatchT{
217 MatchNumber: match.MatchNumber,
218 SetNumber: match.SetNumber,
219 CompLevel: match.CompLevel,
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800220 DataScouted: &request_all_matches_response.ScoutedLevelT{},
Emily Markovabf24c9e2023-02-08 20:31:11 -0800221 }
222 }
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800223
Emily Markovab8551572023-03-22 19:49:39 -0700224 var team *string
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800225 var dataScoutedTeam *bool
226
227 // Fill in the field for the match that we have in in the
228 // database. In the database, each match row only has 1 team
229 // number.
Emily Markovabf24c9e2023-02-08 20:31:11 -0800230 switch match.Alliance {
231 case "R":
232 switch match.AlliancePosition {
233 case 1:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800234 team = &entry.R1
235 dataScoutedTeam = &entry.DataScouted.R1
Emily Markovabf24c9e2023-02-08 20:31:11 -0800236 case 2:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800237 team = &entry.R2
238 dataScoutedTeam = &entry.DataScouted.R2
Emily Markovabf24c9e2023-02-08 20:31:11 -0800239 case 3:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800240 team = &entry.R3
241 dataScoutedTeam = &entry.DataScouted.R3
Emily Markovabf24c9e2023-02-08 20:31:11 -0800242 default:
243 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown red position ", strconv.Itoa(int(match.AlliancePosition)), " in match ", strconv.Itoa(int(match.MatchNumber))))
244 return
245 }
246 case "B":
247 switch match.AlliancePosition {
248 case 1:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800249 team = &entry.B1
250 dataScoutedTeam = &entry.DataScouted.B1
Emily Markovabf24c9e2023-02-08 20:31:11 -0800251 case 2:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800252 team = &entry.B2
253 dataScoutedTeam = &entry.DataScouted.B2
Emily Markovabf24c9e2023-02-08 20:31:11 -0800254 case 3:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800255 team = &entry.B3
256 dataScoutedTeam = &entry.DataScouted.B3
Emily Markovabf24c9e2023-02-08 20:31:11 -0800257 default:
258 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown blue position ", strconv.Itoa(int(match.AlliancePosition)), " in match ", strconv.Itoa(int(match.MatchNumber))))
259 return
260 }
261 default:
262 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown alliance ", match.Alliance, " in match ", strconv.Itoa(int(match.AlliancePosition))))
263 return
264 }
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800265
266 *team = match.TeamNumber
267
268 // Figure out if this team has been data scouted already.
269 *dataScoutedTeam, err = handler.teamHasBeenDataScouted(key, match.TeamNumber)
270 if err != nil {
271 respondWithError(w, http.StatusInternalServerError, fmt.Sprint(
272 "Failed to determine data scouting status for team ",
273 strconv.Itoa(int(match.AlliancePosition)),
274 " in match ",
275 strconv.Itoa(int(match.MatchNumber)),
276 err))
277 return
278 }
279
Emily Markovabf24c9e2023-02-08 20:31:11 -0800280 assembledMatches[key] = entry
281 }
282
283 var response RequestAllMatchesResponseT
284 for _, match := range assembledMatches {
285 copied_match := match
286 response.MatchList = append(response.MatchList, &copied_match)
287 }
288
289 var MATCH_TYPE_ORDERING = []string{"qm", "ef", "qf", "sf", "f"}
290
291 err = nil
292 sort.Slice(response.MatchList, func(i, j int) bool {
293 if err != nil {
294 return false
295 }
296 a := response.MatchList[i]
297 b := response.MatchList[j]
298
Emily Markovaabcac6e2023-02-18 17:50:03 -0800299 aMatchTypeIndex, err2 := findIndexInList(MATCH_TYPE_ORDERING, a.CompLevel)
300 if err2 != nil {
301 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 -0800302 return false
303 }
Emily Markovaabcac6e2023-02-18 17:50:03 -0800304 bMatchTypeIndex, err2 := findIndexInList(MATCH_TYPE_ORDERING, b.CompLevel)
305 if err2 != nil {
306 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 -0800307 return false
308 }
309
310 if aMatchTypeIndex < bMatchTypeIndex {
311 return true
312 }
313 if aMatchTypeIndex > bMatchTypeIndex {
314 return false
315 }
316
317 // Then sort by match number. E.g. in semi finals, all match 1 rounds
318 // are done first. Then come match 2 rounds. And then, if necessary,
319 // the match 3 rounds.
320 aMatchNumber := a.MatchNumber
321 bMatchNumber := b.MatchNumber
322 if aMatchNumber < bMatchNumber {
323 return true
324 }
325 if aMatchNumber > bMatchNumber {
326 return false
327 }
328 // Lastly, sort by set number. I.e. Semi Final 1 Match 1 happens first.
329 // Then comes Semi Final 2 Match 1. Then comes Semi Final 1 Match 2. Then
330 // Semi Final 2 Match 2.
331 aSetNumber := a.SetNumber
332 bSetNumber := b.SetNumber
333 if aSetNumber < bSetNumber {
334 return true
335 }
336 if aSetNumber > bSetNumber {
337 return false
338 }
339 return true
340 })
341
342 if err != nil {
343 // check if error happened during sorting and notify webpage if that
344 respondWithError(w, http.StatusInternalServerError, fmt.Sprint(err))
345 return
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800346 }
347
348 builder := flatbuffers.NewBuilder(50 * 1024)
349 builder.Finish((&response).Pack(builder))
350 w.Write(builder.FinishedBytes())
351}
352
Alex Perry81f96ba2022-03-13 18:26:19 -0700353type submitNoteScoutingHandler struct {
354 db Database
355}
356
357func (handler submitNoteScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
358 requestBytes, err := io.ReadAll(req.Body)
359 if err != nil {
360 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
361 return
362 }
363
Philipp Schraderb7e75932022-03-26 16:18:34 -0700364 request, success := parseRequest(w, requestBytes, "SubmitNotes", submit_notes.GetRootAsSubmitNotes)
Alex Perry81f96ba2022-03-13 18:26:19 -0700365 if !success {
366 return
367 }
368
Filip Kujawaf947cb42022-11-21 10:00:30 -0800369 err = handler.db.AddNotes(db.NotesData{
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800370 TeamNumber: request.Team(),
371 Notes: string(request.Notes()),
372 GoodDriving: bool(request.GoodDriving()),
373 BadDriving: bool(request.BadDriving()),
Filip Kujawa11dc4c92023-04-13 08:55:43 -0700374 SolidPlacing: bool(request.SolidPlacing()),
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800375 SketchyPlacing: bool(request.SketchyPlacing()),
376 GoodDefense: bool(request.GoodDefense()),
377 BadDefense: bool(request.BadDefense()),
378 EasilyDefended: bool(request.EasilyDefended()),
Filip Kujawaf947cb42022-11-21 10:00:30 -0800379 })
Alex Perry81f96ba2022-03-13 18:26:19 -0700380 if err != nil {
381 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert notes: %v", err))
382 return
383 }
384
385 var response SubmitNotesResponseT
386 builder := flatbuffers.NewBuilder(10)
387 builder.Finish((&response).Pack(builder))
388 w.Write(builder.FinishedBytes())
389}
390
Emily Markovafaecfe12023-07-01 12:40:03 -0700391type submitPitImageScoutingHandler struct {
392 db Database
393}
394
395func (handler submitPitImageScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
396 requestBytes, err := io.ReadAll(req.Body)
397 if err != nil {
398 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
399 return
400 }
401
402 request, success := parseRequest(w, requestBytes, "SubmitPitImage", submit_pit_image.GetRootAsSubmitPitImage)
403 if !success {
404 return
405 }
406
407 err = handler.db.AddPitImage(db.PitImage{
408 TeamNumber: string(request.TeamNumber()),
409 CheckSum: db.ComputeSha256FromByteArray(request.ImageDataBytes()),
410 ImagePath: string(request.ImagePath()),
411 ImageData: request.ImageDataBytes(),
412 })
413 if err != nil {
414 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert notes: %v", err))
415 return
416 }
417
418 var response SubmitPitImageResponseT
419 builder := flatbuffers.NewBuilder(10)
420 builder.Finish((&response).Pack(builder))
421 w.Write(builder.FinishedBytes())
422}
423
Emily Markova1abe9782023-03-11 19:45:38 -0800424func ConvertActionsToStat(submitActions *submit_actions.SubmitActions) (db.Stats2023, error) {
425 overall_time := int64(0)
426 cycles := int64(0)
427 picked_up := false
428 lastPlacedTime := int64(0)
Philipp Schrader4b489222023-04-15 16:40:16 -0700429 stat := db.Stats2023{
430 PreScouting: submitActions.PreScouting(),
431 TeamNumber: string(submitActions.TeamNumber()), MatchNumber: submitActions.MatchNumber(), SetNumber: submitActions.SetNumber(), CompLevel: string(submitActions.CompLevel()),
Emily Markova1abe9782023-03-11 19:45:38 -0800432 StartingQuadrant: 0, LowCubesAuto: 0, MiddleCubesAuto: 0, HighCubesAuto: 0, CubesDroppedAuto: 0,
433 LowConesAuto: 0, MiddleConesAuto: 0, HighConesAuto: 0, ConesDroppedAuto: 0, LowCubes: 0, MiddleCubes: 0, HighCubes: 0,
Philipp Schradere11114f2023-04-15 17:04:25 -0700434 CubesDropped: 0, LowCones: 0, MiddleCones: 0, HighCones: 0, ConesDropped: 0, SuperchargedPieces: 0, AvgCycle: 0, CollectedBy: "",
Emily Markova1abe9782023-03-11 19:45:38 -0800435 }
436 // Loop over all actions.
437 for i := 0; i < submitActions.ActionsListLength(); i++ {
438 var action submit_actions.Action
439 if !submitActions.ActionsList(&action, i) {
440 return db.Stats2023{}, errors.New(fmt.Sprintf("Failed to parse submit_actions.Action"))
441 }
442 actionTable := new(flatbuffers.Table)
443 action_type := action.ActionTakenType()
444 if !action.ActionTaken(actionTable) {
445 return db.Stats2023{}, errors.New(fmt.Sprint("Failed to parse sub-action or sub-action was missing"))
446 }
447 if action_type == submit_actions.ActionTypeStartMatchAction {
448 var startMatchAction submit_actions.StartMatchAction
449 startMatchAction.Init(actionTable.Bytes, actionTable.Pos)
450 stat.StartingQuadrant = startMatchAction.Position()
Filip Kujawa0b4b1e52023-04-15 14:05:40 -0700451 } else if action_type == submit_actions.ActionTypeMobilityAction {
452 var mobilityAction submit_actions.MobilityAction
453 mobilityAction.Init(actionTable.Bytes, actionTable.Pos)
454 if mobilityAction.Mobility() {
455 stat.Mobility = true
456 }
457
Emily Markova46a69bf2023-03-22 20:45:52 -0700458 } else if action_type == submit_actions.ActionTypeAutoBalanceAction {
459 var autoBalanceAction submit_actions.AutoBalanceAction
460 autoBalanceAction.Init(actionTable.Bytes, actionTable.Pos)
461 if autoBalanceAction.Docked() {
462 stat.DockedAuto = true
463 }
464 if autoBalanceAction.Engaged() {
465 stat.EngagedAuto = true
466 }
Emily Markova63c63f62023-03-29 20:57:35 -0700467 if autoBalanceAction.BalanceAttempt() {
468 stat.BalanceAttemptAuto = true
469 }
Emily Markova1abe9782023-03-11 19:45:38 -0800470 } else if action_type == submit_actions.ActionTypePickupObjectAction {
471 var pick_up_action submit_actions.PickupObjectAction
472 pick_up_action.Init(actionTable.Bytes, actionTable.Pos)
473 if picked_up == true {
474 object := pick_up_action.ObjectType().String()
475 auto := pick_up_action.Auto()
476 if object == "kCube" && auto == false {
477 stat.CubesDropped += 1
478 } else if object == "kCube" && auto == true {
479 stat.CubesDroppedAuto += 1
480 } else if object == "kCone" && auto == false {
481 stat.ConesDropped += 1
482 } else if object == "kCube" && auto == true {
483 stat.ConesDroppedAuto += 1
484 }
485 } else {
486 picked_up = true
487 }
488 } else if action_type == submit_actions.ActionTypePlaceObjectAction {
489 var place_action submit_actions.PlaceObjectAction
490 place_action.Init(actionTable.Bytes, actionTable.Pos)
491 if !picked_up {
492 return db.Stats2023{}, errors.New(fmt.Sprintf("Got PlaceObjectAction without corresponding PickupObjectAction"))
493 }
494 object := place_action.ObjectType()
495 level := place_action.ScoreLevel()
496 auto := place_action.Auto()
497 if object == 0 && level == 0 && auto == true {
498 stat.LowCubesAuto += 1
499 } else if object == 0 && level == 0 && auto == false {
500 stat.LowCubes += 1
501 } else if object == 0 && level == 1 && auto == true {
502 stat.MiddleCubesAuto += 1
503 } else if object == 0 && level == 1 && auto == false {
504 stat.MiddleCubes += 1
505 } else if object == 0 && level == 2 && auto == true {
506 stat.HighCubesAuto += 1
507 } else if object == 0 && level == 2 && auto == false {
508 stat.HighCubes += 1
509 } else if object == 1 && level == 0 && auto == true {
510 stat.LowConesAuto += 1
511 } else if object == 1 && level == 0 && auto == false {
512 stat.LowCones += 1
513 } else if object == 1 && level == 1 && auto == true {
514 stat.MiddleConesAuto += 1
515 } else if object == 1 && level == 1 && auto == false {
516 stat.MiddleCones += 1
517 } else if object == 1 && level == 2 && auto == true {
518 stat.HighConesAuto += 1
519 } else if object == 1 && level == 2 && auto == false {
520 stat.HighCones += 1
Filip Kujawa7a045e72023-04-13 08:41:09 -0700521 } else if level == 3 {
522 stat.SuperchargedPieces += 1
Emily Markova1abe9782023-03-11 19:45:38 -0800523 } else {
524 return db.Stats2023{}, errors.New(fmt.Sprintf("Got unknown ObjectType/ScoreLevel/Auto combination"))
525 }
526 picked_up = false
527 if lastPlacedTime != int64(0) {
528 // If this is not the first time we place,
529 // start counting cycle time. We define cycle
530 // time as the time between placements.
531 overall_time += int64(action.Timestamp()) - lastPlacedTime
532 cycles += 1
533 }
534 lastPlacedTime = int64(action.Timestamp())
Emily Markova46a69bf2023-03-22 20:45:52 -0700535 } else if action_type == submit_actions.ActionTypeEndMatchAction {
536 var endMatchAction submit_actions.EndMatchAction
537 endMatchAction.Init(actionTable.Bytes, actionTable.Pos)
538 if endMatchAction.Docked() {
539 stat.Docked = true
540 }
541 if endMatchAction.Engaged() {
542 stat.Engaged = true
543 }
Emily Markova63c63f62023-03-29 20:57:35 -0700544 if endMatchAction.BalanceAttempt() {
545 stat.BalanceAttempt = true
546 }
Emily Markova1abe9782023-03-11 19:45:38 -0800547 }
548 }
549 if cycles != 0 {
Philipp Schrader8c878a22023-03-20 22:36:38 -0700550 stat.AvgCycle = overall_time / cycles
Emily Markova1abe9782023-03-11 19:45:38 -0800551 } else {
552 stat.AvgCycle = 0
553 }
554 return stat, nil
555}
556
Emily Markova290147d2023-03-03 22:40:06 -0800557// Handles a Request2023DataScouting request.
558type request2023DataScoutingHandler struct {
559 db Database
560}
561
562func (handler request2023DataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
563 requestBytes, err := io.ReadAll(req.Body)
564 if err != nil {
565 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
566 return
567 }
568
569 _, success := parseRequest(w, requestBytes, "Request2023DataScouting", request_2023_data_scouting.GetRootAsRequest2023DataScouting)
570 if !success {
571 return
572 }
573
574 stats, err := handler.db.ReturnStats2023()
575 if err != nil {
576 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
577 return
578 }
579
580 var response Request2023DataScoutingResponseT
581 for _, stat := range stats {
582 response.StatsList = append(response.StatsList, &request_2023_data_scouting_response.Stats2023T{
Emily Markova63c63f62023-03-29 20:57:35 -0700583 TeamNumber: stat.TeamNumber,
584 MatchNumber: stat.MatchNumber,
585 SetNumber: stat.SetNumber,
586 CompLevel: stat.CompLevel,
587 StartingQuadrant: stat.StartingQuadrant,
588 LowCubesAuto: stat.LowCubesAuto,
589 MiddleCubesAuto: stat.MiddleCubesAuto,
590 HighCubesAuto: stat.HighCubesAuto,
591 CubesDroppedAuto: stat.CubesDroppedAuto,
592 LowConesAuto: stat.LowConesAuto,
593 MiddleConesAuto: stat.MiddleConesAuto,
594 HighConesAuto: stat.HighConesAuto,
595 ConesDroppedAuto: stat.ConesDroppedAuto,
596 LowCubes: stat.LowCubes,
597 MiddleCubes: stat.MiddleCubes,
598 HighCubes: stat.HighCubes,
599 CubesDropped: stat.CubesDropped,
600 LowCones: stat.LowCones,
601 MiddleCones: stat.MiddleCones,
602 HighCones: stat.HighCones,
603 ConesDropped: stat.ConesDropped,
Filip Kujawa7a045e72023-04-13 08:41:09 -0700604 SuperchargedPieces: stat.SuperchargedPieces,
Emily Markova63c63f62023-03-29 20:57:35 -0700605 AvgCycle: stat.AvgCycle,
Filip Kujawa0b4b1e52023-04-15 14:05:40 -0700606 Mobility: stat.Mobility,
Emily Markova63c63f62023-03-29 20:57:35 -0700607 DockedAuto: stat.DockedAuto,
608 EngagedAuto: stat.EngagedAuto,
609 BalanceAttemptAuto: stat.BalanceAttemptAuto,
610 Docked: stat.Docked,
611 Engaged: stat.Engaged,
612 BalanceAttempt: stat.BalanceAttempt,
613 CollectedBy: stat.CollectedBy,
Emily Markova290147d2023-03-03 22:40:06 -0800614 })
615 }
616
617 builder := flatbuffers.NewBuilder(50 * 1024)
618 builder.Finish((&response).Pack(builder))
619 w.Write(builder.FinishedBytes())
620}
621
Emily Markovafaecfe12023-07-01 12:40:03 -0700622type requestPitImagesHandler struct {
623 db Database
624}
625
626func (handler requestPitImagesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
627 requestBytes, err := io.ReadAll(req.Body)
628 if err != nil {
629 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
630 return
631 }
632
633 request, success := parseRequest(w, requestBytes, "RequestPitImages", request_pit_images.GetRootAsRequestPitImages)
634 if !success {
635 return
636 }
637
638 images, err := handler.db.QueryPitImages(string(request.TeamNumber()))
639 if err != nil {
640 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query pit images: %v", err))
641 return
642 }
643
644 var response RequestPitImagesResponseT
645 for _, data := range images {
646 response.PitImageList = append(response.PitImageList, &request_pit_images_response.PitImageT{
647 TeamNumber: data.TeamNumber,
648 ImagePath: data.ImagePath,
649 CheckSum: data.CheckSum,
650 })
651 }
652
653 builder := flatbuffers.NewBuilder(1024)
654 builder.Finish((&response).Pack(builder))
655 w.Write(builder.FinishedBytes())
656}
657
Alex Perry81f96ba2022-03-13 18:26:19 -0700658type requestNotesForTeamHandler struct {
659 db Database
660}
661
662func (handler requestNotesForTeamHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
663 requestBytes, err := io.ReadAll(req.Body)
664 if err != nil {
665 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
666 return
667 }
668
Philipp Schraderb7e75932022-03-26 16:18:34 -0700669 request, success := parseRequest(w, requestBytes, "RequestNotesForTeam", request_notes_for_team.GetRootAsRequestNotesForTeam)
Alex Perry81f96ba2022-03-13 18:26:19 -0700670 if !success {
671 return
672 }
673
Philipp Schradereecb8962022-06-01 21:02:42 -0700674 notes, err := handler.db.QueryNotes(request.Team())
Alex Perry81f96ba2022-03-13 18:26:19 -0700675 if err != nil {
676 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query notes: %v", err))
677 return
678 }
679
680 var response RequestNotesForTeamResponseT
Philipp Schradereecb8962022-06-01 21:02:42 -0700681 for _, data := range notes {
Alex Perry81f96ba2022-03-13 18:26:19 -0700682 response.Notes = append(response.Notes, &request_notes_for_team_response.NoteT{data})
683 }
684
685 builder := flatbuffers.NewBuilder(1024)
686 builder.Finish((&response).Pack(builder))
687 w.Write(builder.FinishedBytes())
688}
689
Milo Lin1d59f0c2022-06-22 20:30:58 -0700690type requestShiftScheduleHandler struct {
691 db Database
692}
693
694func (handler requestShiftScheduleHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
695 requestBytes, err := io.ReadAll(req.Body)
696 if err != nil {
697 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
698 return
699 }
700
701 _, success := parseRequest(w, requestBytes, "RequestShiftSchedule", request_shift_schedule.GetRootAsRequestShiftSchedule)
702 if !success {
703 return
704 }
705
706 shiftData, err := handler.db.ReturnAllShifts()
707 if err != nil {
708 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query shift schedule: %v", err))
709 return
710 }
711
712 var response RequestShiftScheduleResponseT
713 for _, shifts := range shiftData {
714 response.ShiftSchedule = append(response.ShiftSchedule, &request_shift_schedule_response.MatchAssignmentT{
715 MatchNumber: shifts.MatchNumber,
Philipp Schrader2ff455b2023-05-03 22:11:50 -0700716 R1Scouter: shifts.R1scouter,
717 R2Scouter: shifts.R2scouter,
718 R3Scouter: shifts.R3scouter,
719 B1Scouter: shifts.B1scouter,
720 B2Scouter: shifts.B2scouter,
721 B3Scouter: shifts.B3scouter,
Milo Lin1d59f0c2022-06-22 20:30:58 -0700722 })
723 }
724
725 builder := flatbuffers.NewBuilder(1024)
726 builder.Finish((&response).Pack(builder))
727 w.Write(builder.FinishedBytes())
728}
729
730type submitShiftScheduleHandler struct {
731 db Database
732}
733
734func (handler submitShiftScheduleHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
735 // Get the username of the person submitting the data.
736 username := parseUsername(req)
737
738 requestBytes, err := io.ReadAll(req.Body)
739 if err != nil {
740 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
741 return
742 }
743
744 request, success := parseRequest[SubmitShiftSchedule](w, requestBytes, "SubmitShiftSchedule", submit_shift_schedule.GetRootAsSubmitShiftSchedule)
745 if !success {
746 return
747 }
748
749 log.Println("Got shift schedule from", username)
750 shift_schedule_length := request.ShiftScheduleLength()
751 for i := 0; i < shift_schedule_length; i++ {
752 var match_assignment submit_shift_schedule.MatchAssignment
753 request.ShiftSchedule(&match_assignment, i)
754 current_shift := db.Shift{
755 MatchNumber: match_assignment.MatchNumber(),
Philipp Schrader2ff455b2023-05-03 22:11:50 -0700756 R1scouter: string(match_assignment.R1Scouter()),
757 R2scouter: string(match_assignment.R2Scouter()),
758 R3scouter: string(match_assignment.R3Scouter()),
759 B1scouter: string(match_assignment.B1Scouter()),
760 B2scouter: string(match_assignment.B2Scouter()),
761 B3scouter: string(match_assignment.B3Scouter()),
Milo Lin1d59f0c2022-06-22 20:30:58 -0700762 }
763 err = handler.db.AddToShift(current_shift)
764 if err != nil {
765 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to submit shift schedule: ", err))
766 return
767 }
768 }
769
770 builder := flatbuffers.NewBuilder(50 * 1024)
771 builder.Finish((&SubmitShiftScheduleResponseT{}).Pack(builder))
772 w.Write(builder.FinishedBytes())
773}
774
Filip Kujawa210a03b2022-11-24 14:41:11 -0800775type SubmitDriverRankingHandler struct {
776 db Database
777}
778
779func (handler SubmitDriverRankingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
780 requestBytes, err := io.ReadAll(req.Body)
781 if err != nil {
782 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
783 return
784 }
785
786 request, success := parseRequest(w, requestBytes, "SubmitDriverRanking", submit_driver_ranking.GetRootAsSubmitDriverRanking)
787 if !success {
788 return
789 }
790
791 err = handler.db.AddDriverRanking(db.DriverRankingData{
792 MatchNumber: request.MatchNumber(),
793 Rank1: request.Rank1(),
794 Rank2: request.Rank2(),
795 Rank3: request.Rank3(),
796 })
797
798 if err != nil {
799 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert driver ranking: %v", err))
800 return
801 }
802
803 var response SubmitDriverRankingResponseT
804 builder := flatbuffers.NewBuilder(10)
805 builder.Finish((&response).Pack(builder))
806 w.Write(builder.FinishedBytes())
807}
808
Filip Kujawaf882e022022-12-14 13:14:08 -0800809type requestAllNotesHandler struct {
810 db Database
811}
812
813func (handler requestAllNotesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
814 requestBytes, err := io.ReadAll(req.Body)
815 if err != nil {
816 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
817 return
818 }
819
820 _, success := parseRequest(w, requestBytes, "RequestAllNotes", request_all_notes.GetRootAsRequestAllNotes)
821 if !success {
822 return
823 }
824
825 notes, err := handler.db.ReturnAllNotes()
826 if err != nil {
827 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
828 return
829 }
830
831 var response RequestAllNotesResponseT
832 for _, note := range notes {
833 response.NoteList = append(response.NoteList, &request_all_notes_response.NoteT{
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800834 Team: note.TeamNumber,
835 Notes: note.Notes,
836 GoodDriving: note.GoodDriving,
837 BadDriving: note.BadDriving,
Filip Kujawa11dc4c92023-04-13 08:55:43 -0700838 SolidPlacing: note.SolidPlacing,
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800839 SketchyPlacing: note.SketchyPlacing,
840 GoodDefense: note.GoodDefense,
841 BadDefense: note.BadDefense,
842 EasilyDefended: note.EasilyDefended,
Filip Kujawaf882e022022-12-14 13:14:08 -0800843 })
844 }
845
846 builder := flatbuffers.NewBuilder(50 * 1024)
847 builder.Finish((&response).Pack(builder))
848 w.Write(builder.FinishedBytes())
849}
850
851type requestAllDriverRankingsHandler struct {
852 db Database
853}
854
855func (handler requestAllDriverRankingsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
856 requestBytes, err := io.ReadAll(req.Body)
857 if err != nil {
858 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
859 return
860 }
861
862 _, success := parseRequest(w, requestBytes, "RequestAllDriverRankings", request_all_driver_rankings.GetRootAsRequestAllDriverRankings)
863 if !success {
864 return
865 }
866
867 rankings, err := handler.db.ReturnAllDriverRankings()
868 if err != nil {
869 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
870 return
871 }
872
873 var response RequestAllDriverRankingsResponseT
874 for _, ranking := range rankings {
875 response.DriverRankingList = append(response.DriverRankingList, &request_all_driver_rankings_response.RankingT{
876 MatchNumber: ranking.MatchNumber,
877 Rank1: ranking.Rank1,
878 Rank2: ranking.Rank2,
879 Rank3: ranking.Rank3,
880 })
881 }
882
883 builder := flatbuffers.NewBuilder(50 * 1024)
884 builder.Finish((&response).Pack(builder))
885 w.Write(builder.FinishedBytes())
886}
887
Sabina Leaver9b4eb312023-02-20 19:58:17 -0800888type submitActionsHandler struct {
889 db Database
890}
891
892func (handler submitActionsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
893 // Get the username of the person submitting the data.
894 username := parseUsername(req)
895
896 requestBytes, err := io.ReadAll(req.Body)
897 if err != nil {
898 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
899 return
900 }
901
902 request, success := parseRequest(w, requestBytes, "SubmitActions", submit_actions.GetRootAsSubmitActions)
903 if !success {
904 return
905 }
906
907 log.Println("Got actions for match", request.MatchNumber(), "team", request.TeamNumber(), "from", username)
908
909 for i := 0; i < request.ActionsListLength(); i++ {
910
911 var action Action
912 request.ActionsList(&action, i)
913
914 dbAction := db.Action{
Philipp Schrader4b489222023-04-15 16:40:16 -0700915 PreScouting: request.PreScouting(),
Sabina Leaver9b4eb312023-02-20 19:58:17 -0800916 TeamNumber: string(request.TeamNumber()),
917 MatchNumber: request.MatchNumber(),
918 SetNumber: request.SetNumber(),
919 CompLevel: string(request.CompLevel()),
920 //TODO: Serialize CompletedAction
921 CompletedAction: []byte{},
Philipp Schrader670a1c82023-05-17 19:42:43 -0700922 Timestamp: action.Timestamp(),
Sabina Leaver9b4eb312023-02-20 19:58:17 -0800923 CollectedBy: username,
924 }
925
926 // Do some error checking.
927 if action.Timestamp() < 0 {
928 respondWithError(w, http.StatusBadRequest, fmt.Sprint(
929 "Invalid timestamp field value of ", action.Timestamp()))
930 return
931 }
932
933 err = handler.db.AddAction(dbAction)
934 if err != nil {
935 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to add action to database: ", err))
936 return
937 }
938 }
939
Philipp Schradere11114f2023-04-15 17:04:25 -0700940 stats, err := ConvertActionsToStat(request)
941 if err != nil {
942 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to convert actions to stats: ", err))
943 return
944 }
945
946 stats.CollectedBy = username
947
948 err = handler.db.AddToStats2023(stats)
949 if err != nil {
950 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to submit stats: ", stats, ": ", err))
951 return
952 }
953
Sabina Leaver9b4eb312023-02-20 19:58:17 -0800954 builder := flatbuffers.NewBuilder(50 * 1024)
955 builder.Finish((&SubmitActionsResponseT{}).Pack(builder))
956 w.Write(builder.FinishedBytes())
957}
958
Filip Kujawac1ded372023-05-27 14:33:43 -0700959type Delete2023DataScoutingHandler struct {
960 db Database
961}
962
963func (handler Delete2023DataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
964 requestBytes, err := io.ReadAll(req.Body)
965 if err != nil {
966 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
967 return
968 }
969
970 request, success := parseRequest(w, requestBytes, "Delete2023DataScouting", delete_2023_data_scouting.GetRootAsDelete2023DataScouting)
971 if !success {
972 return
973 }
974
975 err = handler.db.DeleteFromStats(
976 string(request.CompLevel()),
977 request.MatchNumber(),
978 request.SetNumber(),
979 string(request.TeamNumber()))
980
981 if err != nil {
982 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to delete from stats: %v", err))
983 return
984 }
985
986 err = handler.db.DeleteFromActions(
987 string(request.CompLevel()),
988 request.MatchNumber(),
989 request.SetNumber(),
990 string(request.TeamNumber()))
991
992 if err != nil {
993 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to delete from actions: %v", err))
994 return
995 }
996
997 var response Delete2023DataScoutingResponseT
998 builder := flatbuffers.NewBuilder(10)
999 builder.Finish((&response).Pack(builder))
1000 w.Write(builder.FinishedBytes())
1001}
1002
Philipp Schrader43c730b2023-02-26 20:27:44 -08001003func HandleRequests(db Database, scoutingServer server.ScoutingServer) {
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08001004 scoutingServer.HandleFunc("/requests", unknown)
Philipp Schradercbf5c6a2022-02-27 23:25:19 -08001005 scoutingServer.Handle("/requests/request/all_matches", requestAllMatchesHandler{db})
Filip Kujawaf882e022022-12-14 13:14:08 -08001006 scoutingServer.Handle("/requests/request/all_notes", requestAllNotesHandler{db})
1007 scoutingServer.Handle("/requests/request/all_driver_rankings", requestAllDriverRankingsHandler{db})
Emily Markova290147d2023-03-03 22:40:06 -08001008 scoutingServer.Handle("/requests/request/2023_data_scouting", request2023DataScoutingHandler{db})
Alex Perry81f96ba2022-03-13 18:26:19 -07001009 scoutingServer.Handle("/requests/submit/submit_notes", submitNoteScoutingHandler{db})
Emily Markovafaecfe12023-07-01 12:40:03 -07001010 scoutingServer.Handle("/requests/submit/submit_pit_image", submitPitImageScoutingHandler{db})
1011 scoutingServer.Handle("/requests/request/pit_images", requestPitImagesHandler{db})
Alex Perry81f96ba2022-03-13 18:26:19 -07001012 scoutingServer.Handle("/requests/request/notes_for_team", requestNotesForTeamHandler{db})
Milo Lin1d59f0c2022-06-22 20:30:58 -07001013 scoutingServer.Handle("/requests/submit/shift_schedule", submitShiftScheduleHandler{db})
1014 scoutingServer.Handle("/requests/request/shift_schedule", requestShiftScheduleHandler{db})
Filip Kujawa210a03b2022-11-24 14:41:11 -08001015 scoutingServer.Handle("/requests/submit/submit_driver_ranking", SubmitDriverRankingHandler{db})
Sabina Leaver9b4eb312023-02-20 19:58:17 -08001016 scoutingServer.Handle("/requests/submit/submit_actions", submitActionsHandler{db})
Filip Kujawac1ded372023-05-27 14:33:43 -07001017 scoutingServer.Handle("/requests/delete/delete_2023_data_scouting", Delete2023DataScoutingHandler{db})
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08001018}