blob: 01f1937210e3089cb963156d801cb46b53c25b8a [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"
Emily Markova8cb91312024-02-02 12:30:37 -080017 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/delete_2024_data_scouting"
18 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/delete_2024_data_scouting_response"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080019 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
Emily Markova290147d2023-03-03 22:40:06 -080020 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_2023_data_scouting"
21 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_2023_data_scouting_response"
Emily Markova8cb91312024-02-02 12:30:37 -080022 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_2024_data_scouting"
23 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_2024_data_scouting_response"
Filip Kujawaf882e022022-12-14 13:14:08 -080024 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_driver_rankings"
25 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_driver_rankings_response"
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080026 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches"
27 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches_response"
Filip Kujawaf882e022022-12-14 13:14:08 -080028 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_notes"
29 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_notes_response"
Emily Markova8e39f452023-12-23 12:17:30 -080030 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_pit_images"
31 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_pit_images_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070032 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team"
33 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team_response"
Emily Markovafaecfe12023-07-01 12:40:03 -070034 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_pit_images"
35 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_pit_images_response"
Milo Lin1d59f0c2022-06-22 20:30:58 -070036 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_shift_schedule"
37 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_shift_schedule_response"
Emily Markova8cb91312024-02-02 12:30:37 -080038 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_2024_actions"
39 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_2024_actions_response"
Sabina Leaver759090b2023-01-14 20:42:56 -080040 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_actions"
41 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_actions_response"
Filip Kujawa210a03b2022-11-24 14:41:11 -080042 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_driver_ranking"
43 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_driver_ranking_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070044 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes"
45 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes_response"
Emily Markovafaecfe12023-07-01 12:40:03 -070046 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_pit_image"
47 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_pit_image_response"
Milo Lin1d59f0c2022-06-22 20:30:58 -070048 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_shift_schedule"
49 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_shift_schedule_response"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080050 "github.com/frc971/971-Robot-Code/scouting/webserver/server"
51 flatbuffers "github.com/google/flatbuffers/go"
52)
53
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080054type RequestAllMatches = request_all_matches.RequestAllMatches
55type RequestAllMatchesResponseT = request_all_matches_response.RequestAllMatchesResponseT
Filip Kujawaf882e022022-12-14 13:14:08 -080056type RequestAllDriverRankings = request_all_driver_rankings.RequestAllDriverRankings
57type RequestAllDriverRankingsResponseT = request_all_driver_rankings_response.RequestAllDriverRankingsResponseT
58type RequestAllNotes = request_all_notes.RequestAllNotes
59type RequestAllNotesResponseT = request_all_notes_response.RequestAllNotesResponseT
Emily Markova290147d2023-03-03 22:40:06 -080060type Request2023DataScouting = request_2023_data_scouting.Request2023DataScouting
61type Request2023DataScoutingResponseT = request_2023_data_scouting_response.Request2023DataScoutingResponseT
Emily Markova8cb91312024-02-02 12:30:37 -080062type Request2024DataScouting = request_2024_data_scouting.Request2024DataScouting
63type Request2024DataScoutingResponseT = request_2024_data_scouting_response.Request2024DataScoutingResponseT
Alex Perry81f96ba2022-03-13 18:26:19 -070064type SubmitNotes = submit_notes.SubmitNotes
65type SubmitNotesResponseT = submit_notes_response.SubmitNotesResponseT
Emily Markovafaecfe12023-07-01 12:40:03 -070066type SubmitPitImage = submit_pit_image.SubmitPitImage
67type SubmitPitImageResponseT = submit_pit_image_response.SubmitPitImageResponseT
68type RequestPitImages = request_pit_images.RequestPitImages
69type RequestPitImagesResponseT = request_pit_images_response.RequestPitImagesResponseT
Emily Markova8e39f452023-12-23 12:17:30 -080070type RequestAllPitImages = request_all_pit_images.RequestAllPitImages
71type RequestAllPitImagesResponseT = request_all_pit_images_response.RequestAllPitImagesResponseT
Alex Perry81f96ba2022-03-13 18:26:19 -070072type RequestNotesForTeam = request_notes_for_team.RequestNotesForTeam
73type RequestNotesForTeamResponseT = request_notes_for_team_response.RequestNotesForTeamResponseT
Milo Lin1d59f0c2022-06-22 20:30:58 -070074type RequestShiftSchedule = request_shift_schedule.RequestShiftSchedule
75type RequestShiftScheduleResponseT = request_shift_schedule_response.RequestShiftScheduleResponseT
76type SubmitShiftSchedule = submit_shift_schedule.SubmitShiftSchedule
77type SubmitShiftScheduleResponseT = submit_shift_schedule_response.SubmitShiftScheduleResponseT
Filip Kujawa210a03b2022-11-24 14:41:11 -080078type SubmitDriverRanking = submit_driver_ranking.SubmitDriverRanking
79type SubmitDriverRankingResponseT = submit_driver_ranking_response.SubmitDriverRankingResponseT
Sabina Leaver759090b2023-01-14 20:42:56 -080080type SubmitActions = submit_actions.SubmitActions
Sabina Leaver9b4eb312023-02-20 19:58:17 -080081type Action = submit_actions.Action
Emily Markova8cb91312024-02-02 12:30:37 -080082type Action2024 = submit_2024_actions.Action
Sabina Leaver759090b2023-01-14 20:42:56 -080083type SubmitActionsResponseT = submit_actions_response.SubmitActionsResponseT
Emily Markova8cb91312024-02-02 12:30:37 -080084type Submit2024Actions = submit_2024_actions.Submit2024Actions
85type Submit2024ActionsResponseT = submit_2024_actions_response.Submit2024ActionsResponseT
Filip Kujawac1ded372023-05-27 14:33:43 -070086type Delete2023DataScouting = delete_2023_data_scouting.Delete2023DataScouting
87type Delete2023DataScoutingResponseT = delete_2023_data_scouting_response.Delete2023DataScoutingResponseT
Emily Markova8cb91312024-02-02 12:30:37 -080088type Delete2024DataScouting = delete_2024_data_scouting.Delete2024DataScouting
89type Delete2024DataScoutingResponseT = delete_2024_data_scouting_response.Delete2024DataScoutingResponseT
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080090
Philipp Schrader8747f1b2022-02-23 23:56:22 -080091// The interface we expect the database abstraction to conform to.
92// We use an interface here because it makes unit testing easier.
93type Database interface {
Emily Markovabf24c9e2023-02-08 20:31:11 -080094 AddToMatch(db.TeamMatch) error
Milo Lin1d59f0c2022-06-22 20:30:58 -070095 AddToShift(db.Shift) error
Emily Markova290147d2023-03-03 22:40:06 -080096 AddToStats2023(db.Stats2023) error
Emily Markova8cb91312024-02-02 12:30:37 -080097 AddToStats2024(db.Stats2024) error
Emily Markovabf24c9e2023-02-08 20:31:11 -080098 ReturnMatches() ([]db.TeamMatch, error)
Filip Kujawaf882e022022-12-14 13:14:08 -080099 ReturnAllNotes() ([]db.NotesData, error)
100 ReturnAllDriverRankings() ([]db.DriverRankingData, error)
Milo Lin1d59f0c2022-06-22 20:30:58 -0700101 ReturnAllShifts() ([]db.Shift, error)
Emily Markova290147d2023-03-03 22:40:06 -0800102 ReturnStats2023() ([]db.Stats2023, error)
Filip Kujawaf3f9def2023-04-20 13:46:46 -0700103 ReturnStats2023ForTeam(teamNumber string, matchNumber int32, setNumber int32, compLevel string, preScouting bool) ([]db.Stats2023, error)
Emily Markova8cb91312024-02-02 12:30:37 -0800104 ReturnStats2024() ([]db.Stats2024, error)
105 ReturnStats2024ForTeam(teamNumber string, matchNumber int32, setNumber int32, compLevel string, preScouting bool) ([]db.Stats2024, error)
Milo Lin1d59f0c2022-06-22 20:30:58 -0700106 QueryAllShifts(int) ([]db.Shift, error)
Emily Markovae68b7632023-12-30 14:17:55 -0800107 QueryNotes(string) ([]string, error)
Emily Markovafaecfe12023-07-01 12:40:03 -0700108 QueryPitImages(string) ([]db.RequestedPitImage, error)
Emily Markova8e39f452023-12-23 12:17:30 -0800109 ReturnPitImages() ([]db.PitImage, error)
Filip Kujawaf947cb42022-11-21 10:00:30 -0800110 AddNotes(db.NotesData) error
Emily Markovafaecfe12023-07-01 12:40:03 -0700111 AddPitImage(db.PitImage) error
Filip Kujawa210a03b2022-11-24 14:41:11 -0800112 AddDriverRanking(db.DriverRankingData) error
Sabina Leaver9b4eb312023-02-20 19:58:17 -0800113 AddAction(db.Action) error
Filip Kujawac1ded372023-05-27 14:33:43 -0700114 DeleteFromStats(string, int32, int32, string) error
Emily Markova8cb91312024-02-02 12:30:37 -0800115 DeleteFromStats2024(string, int32, int32, string) error
Filip Kujawac1ded372023-05-27 14:33:43 -0700116 DeleteFromActions(string, int32, int32, string) error
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800117}
118
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800119// Handles unknown requests. Just returns a 404.
120func unknown(w http.ResponseWriter, req *http.Request) {
121 w.WriteHeader(http.StatusNotFound)
122}
123
124func respondWithError(w http.ResponseWriter, statusCode int, errorMessage string) {
125 builder := flatbuffers.NewBuilder(1024)
126 builder.Finish((&error_response.ErrorResponseT{
127 ErrorMessage: errorMessage,
128 }).Pack(builder))
129 w.WriteHeader(statusCode)
130 w.Write(builder.FinishedBytes())
131}
132
133func respondNotImplemented(w http.ResponseWriter) {
134 respondWithError(w, http.StatusNotImplemented, "")
135}
136
Philipp Schraderb7e75932022-03-26 16:18:34 -0700137func 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 -0800138 success := true
139 defer func() {
140 if r := recover(); r != nil {
Philipp Schraderb7e75932022-03-26 16:18:34 -0700141 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse %s: %v", requestName, r))
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800142 success = false
143 }
144 }()
Philipp Schraderb7e75932022-03-26 16:18:34 -0700145 result := parser(buf, 0)
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800146 return result, success
147}
148
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700149// Parses the authorization information that the browser inserts into the
150// headers. The authorization follows this format:
151//
Philipp Schrader35bb1532023-03-05 13:49:12 -0800152// req.Headers["Authorization"] = []string{"Basic <base64 encoded username:password>"}
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700153func parseUsername(req *http.Request) string {
154 auth, ok := req.Header["Authorization"]
155 if !ok {
156 return "unknown"
157 }
158
159 parts := strings.Split(auth[0], " ")
160 if !(len(parts) == 2 && parts[0] == "Basic") {
161 return "unknown"
162 }
163
164 info, err := base64.StdEncoding.DecodeString(parts[1])
165 if err != nil {
166 log.Println("ERROR: Failed to parse Basic authentication.")
167 return "unknown"
168 }
169
170 loginParts := strings.Split(string(info), ":")
171 if len(loginParts) != 2 {
172 return "unknown"
173 }
174 return loginParts[0]
175}
176
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800177// Handles a RequestAllMaches request.
178type requestAllMatchesHandler struct {
179 db Database
180}
181
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800182// Change structure of match objects in the database(1 per team) to
183// the old match structure(1 per match) that the webserver uses.
184// We use the information in this struct to identify which match object
185// corresponds to which old match structure object.
186type MatchAssemblyKey struct {
187 MatchNumber int32
188 SetNumber int32
189 CompLevel string
190}
191
Emily Markovabf24c9e2023-02-08 20:31:11 -0800192func findIndexInList(list []string, comp_level string) (int, error) {
193 for index, value := range list {
194 if value == comp_level {
195 return index, nil
196 }
197 }
198 return -1, errors.New(fmt.Sprint("Failed to find comp level ", comp_level, " in list ", list))
199}
200
Emily Markovab8551572023-03-22 19:49:39 -0700201func (handler requestAllMatchesHandler) teamHasBeenDataScouted(key MatchAssemblyKey, teamNumber string) (bool, error) {
Emily Markovadcadcb62024-02-03 13:07:17 -0800202 stats, err := handler.db.ReturnStats2024ForTeam(
Filip Kujawaf3f9def2023-04-20 13:46:46 -0700203 teamNumber, key.MatchNumber, key.SetNumber, key.CompLevel, false)
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800204 if err != nil {
205 return false, err
206 }
207 return (len(stats) > 0), nil
208}
209
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800210func (handler requestAllMatchesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
211 requestBytes, err := io.ReadAll(req.Body)
212 if err != nil {
213 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
214 return
215 }
216
Philipp Schraderb7e75932022-03-26 16:18:34 -0700217 _, success := parseRequest(w, requestBytes, "RequestAllMatches", request_all_matches.GetRootAsRequestAllMatches)
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800218 if !success {
219 return
220 }
221
222 matches, err := handler.db.ReturnMatches()
223 if err != nil {
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700224 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
Philipp Schrader2e7eb0002022-03-02 22:52:39 -0800225 return
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800226 }
227
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800228 assembledMatches := map[MatchAssemblyKey]request_all_matches_response.MatchT{}
Emily Markovabf24c9e2023-02-08 20:31:11 -0800229
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800230 for _, match := range matches {
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800231 key := MatchAssemblyKey{match.MatchNumber, match.SetNumber, match.CompLevel}
232
233 // Retrieve the converted match structure we have assembled so
234 // far. If we haven't started assembling one yet, then start a
235 // new one.
Emily Markovabf24c9e2023-02-08 20:31:11 -0800236 entry, ok := assembledMatches[key]
237 if !ok {
238 entry = request_all_matches_response.MatchT{
239 MatchNumber: match.MatchNumber,
240 SetNumber: match.SetNumber,
241 CompLevel: match.CompLevel,
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800242 DataScouted: &request_all_matches_response.ScoutedLevelT{},
Emily Markovabf24c9e2023-02-08 20:31:11 -0800243 }
244 }
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800245
Emily Markovab8551572023-03-22 19:49:39 -0700246 var team *string
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800247 var dataScoutedTeam *bool
248
249 // Fill in the field for the match that we have in in the
250 // database. In the database, each match row only has 1 team
251 // number.
Emily Markovabf24c9e2023-02-08 20:31:11 -0800252 switch match.Alliance {
253 case "R":
254 switch match.AlliancePosition {
255 case 1:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800256 team = &entry.R1
257 dataScoutedTeam = &entry.DataScouted.R1
Emily Markovabf24c9e2023-02-08 20:31:11 -0800258 case 2:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800259 team = &entry.R2
260 dataScoutedTeam = &entry.DataScouted.R2
Emily Markovabf24c9e2023-02-08 20:31:11 -0800261 case 3:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800262 team = &entry.R3
263 dataScoutedTeam = &entry.DataScouted.R3
Emily Markovabf24c9e2023-02-08 20:31:11 -0800264 default:
265 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown red position ", strconv.Itoa(int(match.AlliancePosition)), " in match ", strconv.Itoa(int(match.MatchNumber))))
266 return
267 }
268 case "B":
269 switch match.AlliancePosition {
270 case 1:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800271 team = &entry.B1
272 dataScoutedTeam = &entry.DataScouted.B1
Emily Markovabf24c9e2023-02-08 20:31:11 -0800273 case 2:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800274 team = &entry.B2
275 dataScoutedTeam = &entry.DataScouted.B2
Emily Markovabf24c9e2023-02-08 20:31:11 -0800276 case 3:
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800277 team = &entry.B3
278 dataScoutedTeam = &entry.DataScouted.B3
Emily Markovabf24c9e2023-02-08 20:31:11 -0800279 default:
280 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown blue position ", strconv.Itoa(int(match.AlliancePosition)), " in match ", strconv.Itoa(int(match.MatchNumber))))
281 return
282 }
283 default:
284 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown alliance ", match.Alliance, " in match ", strconv.Itoa(int(match.AlliancePosition))))
285 return
286 }
Philipp Schrader0f7b6362023-03-11 14:02:48 -0800287
288 *team = match.TeamNumber
289
290 // Figure out if this team has been data scouted already.
291 *dataScoutedTeam, err = handler.teamHasBeenDataScouted(key, match.TeamNumber)
292 if err != nil {
293 respondWithError(w, http.StatusInternalServerError, fmt.Sprint(
294 "Failed to determine data scouting status for team ",
295 strconv.Itoa(int(match.AlliancePosition)),
296 " in match ",
297 strconv.Itoa(int(match.MatchNumber)),
298 err))
299 return
300 }
301
Emily Markovabf24c9e2023-02-08 20:31:11 -0800302 assembledMatches[key] = entry
303 }
304
305 var response RequestAllMatchesResponseT
306 for _, match := range assembledMatches {
307 copied_match := match
308 response.MatchList = append(response.MatchList, &copied_match)
309 }
310
311 var MATCH_TYPE_ORDERING = []string{"qm", "ef", "qf", "sf", "f"}
312
313 err = nil
314 sort.Slice(response.MatchList, func(i, j int) bool {
315 if err != nil {
316 return false
317 }
318 a := response.MatchList[i]
319 b := response.MatchList[j]
320
Emily Markovaabcac6e2023-02-18 17:50:03 -0800321 aMatchTypeIndex, err2 := findIndexInList(MATCH_TYPE_ORDERING, a.CompLevel)
322 if err2 != nil {
323 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 -0800324 return false
325 }
Emily Markovaabcac6e2023-02-18 17:50:03 -0800326 bMatchTypeIndex, err2 := findIndexInList(MATCH_TYPE_ORDERING, b.CompLevel)
327 if err2 != nil {
328 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 -0800329 return false
330 }
331
332 if aMatchTypeIndex < bMatchTypeIndex {
333 return true
334 }
335 if aMatchTypeIndex > bMatchTypeIndex {
336 return false
337 }
338
339 // Then sort by match number. E.g. in semi finals, all match 1 rounds
340 // are done first. Then come match 2 rounds. And then, if necessary,
341 // the match 3 rounds.
342 aMatchNumber := a.MatchNumber
343 bMatchNumber := b.MatchNumber
344 if aMatchNumber < bMatchNumber {
345 return true
346 }
347 if aMatchNumber > bMatchNumber {
348 return false
349 }
350 // Lastly, sort by set number. I.e. Semi Final 1 Match 1 happens first.
351 // Then comes Semi Final 2 Match 1. Then comes Semi Final 1 Match 2. Then
352 // Semi Final 2 Match 2.
353 aSetNumber := a.SetNumber
354 bSetNumber := b.SetNumber
355 if aSetNumber < bSetNumber {
356 return true
357 }
358 if aSetNumber > bSetNumber {
359 return false
360 }
361 return true
362 })
363
364 if err != nil {
365 // check if error happened during sorting and notify webpage if that
366 respondWithError(w, http.StatusInternalServerError, fmt.Sprint(err))
367 return
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800368 }
369
370 builder := flatbuffers.NewBuilder(50 * 1024)
371 builder.Finish((&response).Pack(builder))
372 w.Write(builder.FinishedBytes())
373}
374
Alex Perry81f96ba2022-03-13 18:26:19 -0700375type submitNoteScoutingHandler struct {
376 db Database
377}
378
379func (handler submitNoteScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
380 requestBytes, err := io.ReadAll(req.Body)
381 if err != nil {
382 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
383 return
384 }
385
Philipp Schraderb7e75932022-03-26 16:18:34 -0700386 request, success := parseRequest(w, requestBytes, "SubmitNotes", submit_notes.GetRootAsSubmitNotes)
Alex Perry81f96ba2022-03-13 18:26:19 -0700387 if !success {
388 return
389 }
390
Filip Kujawaf947cb42022-11-21 10:00:30 -0800391 err = handler.db.AddNotes(db.NotesData{
Emily Markovae68b7632023-12-30 14:17:55 -0800392 TeamNumber: string(request.Team()),
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800393 Notes: string(request.Notes()),
394 GoodDriving: bool(request.GoodDriving()),
395 BadDriving: bool(request.BadDriving()),
Filip Kujawa11dc4c92023-04-13 08:55:43 -0700396 SolidPlacing: bool(request.SolidPlacing()),
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800397 SketchyPlacing: bool(request.SketchyPlacing()),
398 GoodDefense: bool(request.GoodDefense()),
399 BadDefense: bool(request.BadDefense()),
400 EasilyDefended: bool(request.EasilyDefended()),
Emily Markovacf893f42024-03-13 19:03:10 -0700401 NoShow: bool(request.NoShow()),
402 MatchNumber: request.MatchNumber(),
403 SetNumber: request.SetNumber(),
404 CompLevel: string(request.CompLevel()),
Filip Kujawaf947cb42022-11-21 10:00:30 -0800405 })
Alex Perry81f96ba2022-03-13 18:26:19 -0700406 if err != nil {
407 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert notes: %v", err))
408 return
409 }
410
411 var response SubmitNotesResponseT
412 builder := flatbuffers.NewBuilder(10)
413 builder.Finish((&response).Pack(builder))
414 w.Write(builder.FinishedBytes())
415}
416
Emily Markovafaecfe12023-07-01 12:40:03 -0700417type submitPitImageScoutingHandler struct {
418 db Database
419}
420
421func (handler submitPitImageScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
422 requestBytes, err := io.ReadAll(req.Body)
423 if err != nil {
424 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
425 return
426 }
427
428 request, success := parseRequest(w, requestBytes, "SubmitPitImage", submit_pit_image.GetRootAsSubmitPitImage)
429 if !success {
430 return
431 }
432
433 err = handler.db.AddPitImage(db.PitImage{
434 TeamNumber: string(request.TeamNumber()),
435 CheckSum: db.ComputeSha256FromByteArray(request.ImageDataBytes()),
436 ImagePath: string(request.ImagePath()),
437 ImageData: request.ImageDataBytes(),
438 })
439 if err != nil {
440 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert notes: %v", err))
441 return
442 }
443
444 var response SubmitPitImageResponseT
445 builder := flatbuffers.NewBuilder(10)
446 builder.Finish((&response).Pack(builder))
447 w.Write(builder.FinishedBytes())
448}
449
Emily Markova8cb91312024-02-02 12:30:37 -0800450func ConvertActionsToStat2024(submit2024Actions *submit_2024_actions.Submit2024Actions) (db.Stats2024, error) {
451 overall_time := int64(0)
452 cycles := int64(0)
453 picked_up := false
454 lastPlacedTime := int64(0)
455 stat := db.Stats2024{
456 PreScouting: submit2024Actions.PreScouting(), TeamNumber: string(submit2024Actions.TeamNumber()), MatchNumber: submit2024Actions.MatchNumber(), SetNumber: submit2024Actions.SetNumber(), CompLevel: string(submit2024Actions.CompLevel()),
457 StartingQuadrant: 0, SpeakerAuto: 0, AmpAuto: 0, NotesDroppedAuto: 0, MobilityAuto: false,
Emily Markovacd156942024-04-07 19:32:28 -0700458 Speaker: 0, Amp: 0, SpeakerAmplified: 0, NotesDropped: 0, Shuttled: 0, OutOfField: 0, Penalties: 0,
Emily Markova040123c2024-02-27 09:48:37 -0800459 TrapNote: false, Spotlight: false, AvgCycle: 0, Park: false, OnStage: false, Harmony: false, RobotDied: false, CollectedBy: "",
Emily Markova8cb91312024-02-02 12:30:37 -0800460 }
461 // Loop over all actions.
462 for i := 0; i < submit2024Actions.ActionsListLength(); i++ {
463 var action submit_2024_actions.Action
464 if !submit2024Actions.ActionsList(&action, i) {
465 return db.Stats2024{}, errors.New(fmt.Sprintf("Failed to parse submit_2024_actions.Action"))
466 }
467 actionTable := new(flatbuffers.Table)
468 action_type := action.ActionTakenType()
469 if !action.ActionTaken(actionTable) {
470 return db.Stats2024{}, errors.New(fmt.Sprint("Failed to parse sub-action or sub-action was missing"))
471 }
472 if action_type == submit_2024_actions.ActionTypeStartMatchAction {
473 var startMatchAction submit_2024_actions.StartMatchAction
474 startMatchAction.Init(actionTable.Bytes, actionTable.Pos)
475 stat.StartingQuadrant = startMatchAction.Position()
476 } else if action_type == submit_2024_actions.ActionTypeMobilityAction {
477 var mobilityAction submit_2024_actions.MobilityAction
478 mobilityAction.Init(actionTable.Bytes, actionTable.Pos)
479 if mobilityAction.Mobility() {
480 stat.MobilityAuto = true
481 }
482
483 } else if action_type == submit_2024_actions.ActionTypePenaltyAction {
484 var penaltyAction submit_2024_actions.PenaltyAction
485 penaltyAction.Init(actionTable.Bytes, actionTable.Pos)
Emily Markovadcadcb62024-02-03 13:07:17 -0800486 stat.Penalties += penaltyAction.Penalties()
Emily Markova8cb91312024-02-02 12:30:37 -0800487
Emily Markova040123c2024-02-27 09:48:37 -0800488 } else if action_type == submit_2024_actions.ActionTypeRobotDeathAction {
489 var robotDeathAction submit_2024_actions.RobotDeathAction
490 robotDeathAction.Init(actionTable.Bytes, actionTable.Pos)
491 stat.RobotDied = true
492
Emily Markova8cb91312024-02-02 12:30:37 -0800493 } else if action_type == submit_2024_actions.ActionTypePickupNoteAction {
494 var pick_up_action submit_2024_actions.PickupNoteAction
495 pick_up_action.Init(actionTable.Bytes, actionTable.Pos)
Emily Markova040123c2024-02-27 09:48:37 -0800496 picked_up = true
Emily Markova8cb91312024-02-02 12:30:37 -0800497 } else if action_type == submit_2024_actions.ActionTypePlaceNoteAction {
498 var place_action submit_2024_actions.PlaceNoteAction
499 place_action.Init(actionTable.Bytes, actionTable.Pos)
500 if !picked_up {
501 return db.Stats2024{}, errors.New(fmt.Sprintf("Got PlaceNoteAction without corresponding PickupObjectAction"))
502 }
503 score_type := place_action.ScoreType()
504 auto := place_action.Auto()
Emily Markovacd156942024-04-07 19:32:28 -0700505 count_in_cycle := true
Emily Markova8cb91312024-02-02 12:30:37 -0800506 if score_type == submit_2024_actions.ScoreTypekAMP && auto {
507 stat.AmpAuto += 1
508 } else if score_type == submit_2024_actions.ScoreTypekAMP && !auto {
509 stat.Amp += 1
Emily Markova8cb91312024-02-02 12:30:37 -0800510 } else if score_type == submit_2024_actions.ScoreTypekSPEAKER && !auto {
511 stat.Speaker += 1
512 } else if score_type == submit_2024_actions.ScoreTypekSPEAKER && auto {
513 stat.SpeakerAuto += 1
514 } else if score_type == submit_2024_actions.ScoreTypekSPEAKER_AMPLIFIED && !auto {
515 stat.SpeakerAmplified += 1
Emily Markova040123c2024-02-27 09:48:37 -0800516 } else if score_type == submit_2024_actions.ScoreTypekDROPPED && auto {
517 stat.NotesDroppedAuto += 1
Emily Markovacd156942024-04-07 19:32:28 -0700518 count_in_cycle = false
Emily Markova040123c2024-02-27 09:48:37 -0800519 } else if score_type == submit_2024_actions.ScoreTypekDROPPED && !auto {
520 stat.NotesDropped += 1
Emily Markovacd156942024-04-07 19:32:28 -0700521 count_in_cycle = false
522 } else if score_type == submit_2024_actions.ScoreTypekSHUTTLED {
523 stat.Shuttled += 1
524 count_in_cycle = false
525 } else if score_type == submit_2024_actions.ScoreTypekOUT_OF_FIELD {
526 stat.OutOfField += 1
527 count_in_cycle = false
Emily Markova8cb91312024-02-02 12:30:37 -0800528 } else {
529 return db.Stats2024{}, errors.New(fmt.Sprintf("Got unknown ObjectType/ScoreLevel/Auto combination"))
530 }
531 picked_up = false
Emily Markovacd156942024-04-07 19:32:28 -0700532 if count_in_cycle {
533 // Assuming dropped, shuttled, and out of field
534 // notes are not counted in total cycle time.
535 if lastPlacedTime != int64(0) {
536 // If this is not the first time we place,
537 // start counting cycle time. We define cycle
538 // time as the time between placements.
539 overall_time += int64(action.Timestamp()) - lastPlacedTime
540 }
Emily Markova8cb91312024-02-02 12:30:37 -0800541 cycles += 1
Emily Markovacd156942024-04-07 19:32:28 -0700542 lastPlacedTime = int64(action.Timestamp())
Emily Markova8cb91312024-02-02 12:30:37 -0800543 }
Emily Markova8cb91312024-02-02 12:30:37 -0800544 } else if action_type == submit_2024_actions.ActionTypeEndMatchAction {
545 var endMatchAction submit_2024_actions.EndMatchAction
546 endMatchAction.Init(actionTable.Bytes, actionTable.Pos)
547 if endMatchAction.StageType() == submit_2024_actions.StageTypekON_STAGE {
548 stat.OnStage = true
549 } else if endMatchAction.StageType() == submit_2024_actions.StageTypekPARK {
550 stat.Park = true
551 } else if endMatchAction.StageType() == submit_2024_actions.StageTypekHARMONY {
552 stat.Harmony = true
553 }
554 stat.TrapNote = endMatchAction.TrapNote()
Emily Markova6079e2f2024-02-17 13:17:24 -0800555 stat.Spotlight = endMatchAction.Spotlight()
Emily Markova8cb91312024-02-02 12:30:37 -0800556 }
557 }
558 if cycles != 0 {
559 stat.AvgCycle = overall_time / cycles
560 } else {
561 stat.AvgCycle = 0
562 }
563 return stat, nil
564}
565
566// Handles a Request2024DataScouting request.
567type request2024DataScoutingHandler struct {
568 db Database
569}
570
571func (handler request2024DataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
572 requestBytes, err := io.ReadAll(req.Body)
573 if err != nil {
574 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
575 return
576 }
577
578 _, success := parseRequest(w, requestBytes, "Request2024DataScouting", request_2024_data_scouting.GetRootAsRequest2024DataScouting)
579 if !success {
580 return
581 }
582
583 stats, err := handler.db.ReturnStats2024()
584 if err != nil {
585 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
586 return
587 }
588
589 var response Request2024DataScoutingResponseT
590 for _, stat := range stats {
591 response.StatsList = append(response.StatsList, &request_2024_data_scouting_response.Stats2024T{
592 TeamNumber: stat.TeamNumber,
593 MatchNumber: stat.MatchNumber,
594 SetNumber: stat.SetNumber,
595 CompLevel: stat.CompLevel,
596 StartingQuadrant: stat.StartingQuadrant,
597 SpeakerAuto: stat.SpeakerAuto,
598 AmpAuto: stat.AmpAuto,
599 NotesDroppedAuto: stat.NotesDroppedAuto,
600 MobilityAuto: stat.MobilityAuto,
601 Speaker: stat.Speaker,
602 Amp: stat.Amp,
603 SpeakerAmplified: stat.SpeakerAmplified,
Emily Markova8cb91312024-02-02 12:30:37 -0800604 NotesDropped: stat.NotesDropped,
Emily Markovacd156942024-04-07 19:32:28 -0700605 Shuttled: stat.Shuttled,
606 OutOfField: stat.OutOfField,
Emily Markova8cb91312024-02-02 12:30:37 -0800607 Penalties: stat.Penalties,
608 TrapNote: stat.TrapNote,
Emily Markova6079e2f2024-02-17 13:17:24 -0800609 Spotlight: stat.Spotlight,
Emily Markova8cb91312024-02-02 12:30:37 -0800610 AvgCycle: stat.AvgCycle,
611 Park: stat.Park,
612 OnStage: stat.OnStage,
613 Harmony: stat.Harmony,
Emily Markova040123c2024-02-27 09:48:37 -0800614 RobotDied: stat.RobotDied,
Emily Markova8cb91312024-02-02 12:30:37 -0800615 CollectedBy: stat.CollectedBy,
616 })
617 }
618
619 builder := flatbuffers.NewBuilder(50 * 1024)
620 builder.Finish((&response).Pack(builder))
621 w.Write(builder.FinishedBytes())
622}
623
Emily Markova1abe9782023-03-11 19:45:38 -0800624func ConvertActionsToStat(submitActions *submit_actions.SubmitActions) (db.Stats2023, error) {
625 overall_time := int64(0)
626 cycles := int64(0)
627 picked_up := false
628 lastPlacedTime := int64(0)
Philipp Schrader4b489222023-04-15 16:40:16 -0700629 stat := db.Stats2023{
630 PreScouting: submitActions.PreScouting(),
631 TeamNumber: string(submitActions.TeamNumber()), MatchNumber: submitActions.MatchNumber(), SetNumber: submitActions.SetNumber(), CompLevel: string(submitActions.CompLevel()),
Emily Markova1abe9782023-03-11 19:45:38 -0800632 StartingQuadrant: 0, LowCubesAuto: 0, MiddleCubesAuto: 0, HighCubesAuto: 0, CubesDroppedAuto: 0,
633 LowConesAuto: 0, MiddleConesAuto: 0, HighConesAuto: 0, ConesDroppedAuto: 0, LowCubes: 0, MiddleCubes: 0, HighCubes: 0,
Philipp Schradere11114f2023-04-15 17:04:25 -0700634 CubesDropped: 0, LowCones: 0, MiddleCones: 0, HighCones: 0, ConesDropped: 0, SuperchargedPieces: 0, AvgCycle: 0, CollectedBy: "",
Emily Markova1abe9782023-03-11 19:45:38 -0800635 }
636 // Loop over all actions.
637 for i := 0; i < submitActions.ActionsListLength(); i++ {
638 var action submit_actions.Action
639 if !submitActions.ActionsList(&action, i) {
640 return db.Stats2023{}, errors.New(fmt.Sprintf("Failed to parse submit_actions.Action"))
641 }
642 actionTable := new(flatbuffers.Table)
643 action_type := action.ActionTakenType()
644 if !action.ActionTaken(actionTable) {
645 return db.Stats2023{}, errors.New(fmt.Sprint("Failed to parse sub-action or sub-action was missing"))
646 }
647 if action_type == submit_actions.ActionTypeStartMatchAction {
648 var startMatchAction submit_actions.StartMatchAction
649 startMatchAction.Init(actionTable.Bytes, actionTable.Pos)
650 stat.StartingQuadrant = startMatchAction.Position()
Filip Kujawa0b4b1e52023-04-15 14:05:40 -0700651 } else if action_type == submit_actions.ActionTypeMobilityAction {
652 var mobilityAction submit_actions.MobilityAction
653 mobilityAction.Init(actionTable.Bytes, actionTable.Pos)
654 if mobilityAction.Mobility() {
655 stat.Mobility = true
656 }
657
Emily Markova46a69bf2023-03-22 20:45:52 -0700658 } else if action_type == submit_actions.ActionTypeAutoBalanceAction {
659 var autoBalanceAction submit_actions.AutoBalanceAction
660 autoBalanceAction.Init(actionTable.Bytes, actionTable.Pos)
661 if autoBalanceAction.Docked() {
662 stat.DockedAuto = true
663 }
664 if autoBalanceAction.Engaged() {
665 stat.EngagedAuto = true
666 }
Emily Markova63c63f62023-03-29 20:57:35 -0700667 if autoBalanceAction.BalanceAttempt() {
668 stat.BalanceAttemptAuto = true
669 }
Emily Markova1abe9782023-03-11 19:45:38 -0800670 } else if action_type == submit_actions.ActionTypePickupObjectAction {
671 var pick_up_action submit_actions.PickupObjectAction
672 pick_up_action.Init(actionTable.Bytes, actionTable.Pos)
673 if picked_up == true {
674 object := pick_up_action.ObjectType().String()
675 auto := pick_up_action.Auto()
676 if object == "kCube" && auto == false {
677 stat.CubesDropped += 1
678 } else if object == "kCube" && auto == true {
679 stat.CubesDroppedAuto += 1
680 } else if object == "kCone" && auto == false {
681 stat.ConesDropped += 1
682 } else if object == "kCube" && auto == true {
683 stat.ConesDroppedAuto += 1
684 }
685 } else {
686 picked_up = true
687 }
688 } else if action_type == submit_actions.ActionTypePlaceObjectAction {
689 var place_action submit_actions.PlaceObjectAction
690 place_action.Init(actionTable.Bytes, actionTable.Pos)
691 if !picked_up {
692 return db.Stats2023{}, errors.New(fmt.Sprintf("Got PlaceObjectAction without corresponding PickupObjectAction"))
693 }
694 object := place_action.ObjectType()
695 level := place_action.ScoreLevel()
696 auto := place_action.Auto()
697 if object == 0 && level == 0 && auto == true {
698 stat.LowCubesAuto += 1
699 } else if object == 0 && level == 0 && auto == false {
700 stat.LowCubes += 1
701 } else if object == 0 && level == 1 && auto == true {
702 stat.MiddleCubesAuto += 1
703 } else if object == 0 && level == 1 && auto == false {
704 stat.MiddleCubes += 1
705 } else if object == 0 && level == 2 && auto == true {
706 stat.HighCubesAuto += 1
707 } else if object == 0 && level == 2 && auto == false {
708 stat.HighCubes += 1
709 } else if object == 1 && level == 0 && auto == true {
710 stat.LowConesAuto += 1
711 } else if object == 1 && level == 0 && auto == false {
712 stat.LowCones += 1
713 } else if object == 1 && level == 1 && auto == true {
714 stat.MiddleConesAuto += 1
715 } else if object == 1 && level == 1 && auto == false {
716 stat.MiddleCones += 1
717 } else if object == 1 && level == 2 && auto == true {
718 stat.HighConesAuto += 1
719 } else if object == 1 && level == 2 && auto == false {
720 stat.HighCones += 1
Filip Kujawa7a045e72023-04-13 08:41:09 -0700721 } else if level == 3 {
722 stat.SuperchargedPieces += 1
Emily Markova1abe9782023-03-11 19:45:38 -0800723 } else {
724 return db.Stats2023{}, errors.New(fmt.Sprintf("Got unknown ObjectType/ScoreLevel/Auto combination"))
725 }
726 picked_up = false
727 if lastPlacedTime != int64(0) {
728 // If this is not the first time we place,
729 // start counting cycle time. We define cycle
730 // time as the time between placements.
731 overall_time += int64(action.Timestamp()) - lastPlacedTime
732 cycles += 1
733 }
734 lastPlacedTime = int64(action.Timestamp())
Emily Markova46a69bf2023-03-22 20:45:52 -0700735 } else if action_type == submit_actions.ActionTypeEndMatchAction {
736 var endMatchAction submit_actions.EndMatchAction
737 endMatchAction.Init(actionTable.Bytes, actionTable.Pos)
738 if endMatchAction.Docked() {
739 stat.Docked = true
740 }
741 if endMatchAction.Engaged() {
742 stat.Engaged = true
743 }
Emily Markova63c63f62023-03-29 20:57:35 -0700744 if endMatchAction.BalanceAttempt() {
745 stat.BalanceAttempt = true
746 }
Emily Markova1abe9782023-03-11 19:45:38 -0800747 }
748 }
749 if cycles != 0 {
Philipp Schrader8c878a22023-03-20 22:36:38 -0700750 stat.AvgCycle = overall_time / cycles
Emily Markova1abe9782023-03-11 19:45:38 -0800751 } else {
752 stat.AvgCycle = 0
753 }
754 return stat, nil
755}
756
Emily Markova290147d2023-03-03 22:40:06 -0800757// Handles a Request2023DataScouting request.
758type request2023DataScoutingHandler struct {
759 db Database
760}
761
762func (handler request2023DataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
763 requestBytes, err := io.ReadAll(req.Body)
764 if err != nil {
765 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
766 return
767 }
768
769 _, success := parseRequest(w, requestBytes, "Request2023DataScouting", request_2023_data_scouting.GetRootAsRequest2023DataScouting)
770 if !success {
771 return
772 }
773
774 stats, err := handler.db.ReturnStats2023()
775 if err != nil {
776 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
777 return
778 }
779
780 var response Request2023DataScoutingResponseT
781 for _, stat := range stats {
782 response.StatsList = append(response.StatsList, &request_2023_data_scouting_response.Stats2023T{
Emily Markova63c63f62023-03-29 20:57:35 -0700783 TeamNumber: stat.TeamNumber,
784 MatchNumber: stat.MatchNumber,
785 SetNumber: stat.SetNumber,
786 CompLevel: stat.CompLevel,
787 StartingQuadrant: stat.StartingQuadrant,
788 LowCubesAuto: stat.LowCubesAuto,
789 MiddleCubesAuto: stat.MiddleCubesAuto,
790 HighCubesAuto: stat.HighCubesAuto,
791 CubesDroppedAuto: stat.CubesDroppedAuto,
792 LowConesAuto: stat.LowConesAuto,
793 MiddleConesAuto: stat.MiddleConesAuto,
794 HighConesAuto: stat.HighConesAuto,
795 ConesDroppedAuto: stat.ConesDroppedAuto,
796 LowCubes: stat.LowCubes,
797 MiddleCubes: stat.MiddleCubes,
798 HighCubes: stat.HighCubes,
799 CubesDropped: stat.CubesDropped,
800 LowCones: stat.LowCones,
801 MiddleCones: stat.MiddleCones,
802 HighCones: stat.HighCones,
803 ConesDropped: stat.ConesDropped,
Filip Kujawa7a045e72023-04-13 08:41:09 -0700804 SuperchargedPieces: stat.SuperchargedPieces,
Emily Markova63c63f62023-03-29 20:57:35 -0700805 AvgCycle: stat.AvgCycle,
Filip Kujawa0b4b1e52023-04-15 14:05:40 -0700806 Mobility: stat.Mobility,
Emily Markova63c63f62023-03-29 20:57:35 -0700807 DockedAuto: stat.DockedAuto,
808 EngagedAuto: stat.EngagedAuto,
809 BalanceAttemptAuto: stat.BalanceAttemptAuto,
810 Docked: stat.Docked,
811 Engaged: stat.Engaged,
812 BalanceAttempt: stat.BalanceAttempt,
813 CollectedBy: stat.CollectedBy,
Emily Markova290147d2023-03-03 22:40:06 -0800814 })
815 }
816
817 builder := flatbuffers.NewBuilder(50 * 1024)
818 builder.Finish((&response).Pack(builder))
819 w.Write(builder.FinishedBytes())
820}
821
Emily Markova8e39f452023-12-23 12:17:30 -0800822type requestAllPitImagesHandler struct {
823 db Database
824}
825
826func (handler requestAllPitImagesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
827 requestBytes, err := io.ReadAll(req.Body)
828 if err != nil {
829 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
830 return
831 }
832
833 _, success := parseRequest(w, requestBytes, "RequestAllPitImages", request_all_pit_images.GetRootAsRequestAllPitImages)
834 if !success {
835 return
836 }
837
838 images, err := handler.db.ReturnPitImages()
839 if err != nil {
840 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to get pit images: %v", err))
841 return
842 }
843
844 var response RequestAllPitImagesResponseT
845 for _, data := range images {
846 response.PitImageList = append(response.PitImageList, &request_all_pit_images_response.PitImageT{
847 TeamNumber: data.TeamNumber,
848 ImagePath: data.ImagePath,
849 CheckSum: data.CheckSum,
850 })
851 }
852
853 builder := flatbuffers.NewBuilder(1024)
854 builder.Finish((&response).Pack(builder))
855 w.Write(builder.FinishedBytes())
856}
857
Emily Markovafaecfe12023-07-01 12:40:03 -0700858type requestPitImagesHandler struct {
859 db Database
860}
861
862func (handler requestPitImagesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
863 requestBytes, err := io.ReadAll(req.Body)
864 if err != nil {
865 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
866 return
867 }
868
869 request, success := parseRequest(w, requestBytes, "RequestPitImages", request_pit_images.GetRootAsRequestPitImages)
870 if !success {
871 return
872 }
873
874 images, err := handler.db.QueryPitImages(string(request.TeamNumber()))
875 if err != nil {
876 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query pit images: %v", err))
877 return
878 }
879
880 var response RequestPitImagesResponseT
881 for _, data := range images {
882 response.PitImageList = append(response.PitImageList, &request_pit_images_response.PitImageT{
883 TeamNumber: data.TeamNumber,
884 ImagePath: data.ImagePath,
885 CheckSum: data.CheckSum,
886 })
887 }
888
889 builder := flatbuffers.NewBuilder(1024)
890 builder.Finish((&response).Pack(builder))
891 w.Write(builder.FinishedBytes())
892}
893
Alex Perry81f96ba2022-03-13 18:26:19 -0700894type requestNotesForTeamHandler struct {
895 db Database
896}
897
898func (handler requestNotesForTeamHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
899 requestBytes, err := io.ReadAll(req.Body)
900 if err != nil {
901 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
902 return
903 }
904
Philipp Schraderb7e75932022-03-26 16:18:34 -0700905 request, success := parseRequest(w, requestBytes, "RequestNotesForTeam", request_notes_for_team.GetRootAsRequestNotesForTeam)
Alex Perry81f96ba2022-03-13 18:26:19 -0700906 if !success {
907 return
908 }
909
Emily Markovae68b7632023-12-30 14:17:55 -0800910 notes, err := handler.db.QueryNotes(string(request.Team()))
Alex Perry81f96ba2022-03-13 18:26:19 -0700911 if err != nil {
912 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query notes: %v", err))
913 return
914 }
915
916 var response RequestNotesForTeamResponseT
Philipp Schradereecb8962022-06-01 21:02:42 -0700917 for _, data := range notes {
Alex Perry81f96ba2022-03-13 18:26:19 -0700918 response.Notes = append(response.Notes, &request_notes_for_team_response.NoteT{data})
919 }
920
921 builder := flatbuffers.NewBuilder(1024)
922 builder.Finish((&response).Pack(builder))
923 w.Write(builder.FinishedBytes())
924}
925
Milo Lin1d59f0c2022-06-22 20:30:58 -0700926type requestShiftScheduleHandler struct {
927 db Database
928}
929
930func (handler requestShiftScheduleHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
931 requestBytes, err := io.ReadAll(req.Body)
932 if err != nil {
933 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
934 return
935 }
936
937 _, success := parseRequest(w, requestBytes, "RequestShiftSchedule", request_shift_schedule.GetRootAsRequestShiftSchedule)
938 if !success {
939 return
940 }
941
942 shiftData, err := handler.db.ReturnAllShifts()
943 if err != nil {
944 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query shift schedule: %v", err))
945 return
946 }
947
948 var response RequestShiftScheduleResponseT
949 for _, shifts := range shiftData {
950 response.ShiftSchedule = append(response.ShiftSchedule, &request_shift_schedule_response.MatchAssignmentT{
951 MatchNumber: shifts.MatchNumber,
Philipp Schrader2ff455b2023-05-03 22:11:50 -0700952 R1Scouter: shifts.R1scouter,
953 R2Scouter: shifts.R2scouter,
954 R3Scouter: shifts.R3scouter,
955 B1Scouter: shifts.B1scouter,
956 B2Scouter: shifts.B2scouter,
957 B3Scouter: shifts.B3scouter,
Milo Lin1d59f0c2022-06-22 20:30:58 -0700958 })
959 }
960
961 builder := flatbuffers.NewBuilder(1024)
962 builder.Finish((&response).Pack(builder))
963 w.Write(builder.FinishedBytes())
964}
965
966type submitShiftScheduleHandler struct {
967 db Database
968}
969
970func (handler submitShiftScheduleHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
971 // Get the username of the person submitting the data.
972 username := parseUsername(req)
973
974 requestBytes, err := io.ReadAll(req.Body)
975 if err != nil {
976 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
977 return
978 }
979
980 request, success := parseRequest[SubmitShiftSchedule](w, requestBytes, "SubmitShiftSchedule", submit_shift_schedule.GetRootAsSubmitShiftSchedule)
981 if !success {
982 return
983 }
984
985 log.Println("Got shift schedule from", username)
986 shift_schedule_length := request.ShiftScheduleLength()
987 for i := 0; i < shift_schedule_length; i++ {
988 var match_assignment submit_shift_schedule.MatchAssignment
989 request.ShiftSchedule(&match_assignment, i)
990 current_shift := db.Shift{
991 MatchNumber: match_assignment.MatchNumber(),
Philipp Schrader2ff455b2023-05-03 22:11:50 -0700992 R1scouter: string(match_assignment.R1Scouter()),
993 R2scouter: string(match_assignment.R2Scouter()),
994 R3scouter: string(match_assignment.R3Scouter()),
995 B1scouter: string(match_assignment.B1Scouter()),
996 B2scouter: string(match_assignment.B2Scouter()),
997 B3scouter: string(match_assignment.B3Scouter()),
Milo Lin1d59f0c2022-06-22 20:30:58 -0700998 }
999 err = handler.db.AddToShift(current_shift)
1000 if err != nil {
1001 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to submit shift schedule: ", err))
1002 return
1003 }
1004 }
1005
1006 builder := flatbuffers.NewBuilder(50 * 1024)
1007 builder.Finish((&SubmitShiftScheduleResponseT{}).Pack(builder))
1008 w.Write(builder.FinishedBytes())
1009}
1010
Filip Kujawa210a03b2022-11-24 14:41:11 -08001011type SubmitDriverRankingHandler struct {
1012 db Database
1013}
1014
1015func (handler SubmitDriverRankingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
1016 requestBytes, err := io.ReadAll(req.Body)
1017 if err != nil {
1018 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
1019 return
1020 }
1021
1022 request, success := parseRequest(w, requestBytes, "SubmitDriverRanking", submit_driver_ranking.GetRootAsSubmitDriverRanking)
1023 if !success {
1024 return
1025 }
1026
1027 err = handler.db.AddDriverRanking(db.DriverRankingData{
1028 MatchNumber: request.MatchNumber(),
Emily Markovae68b7632023-12-30 14:17:55 -08001029 Rank1: string(request.Rank1()),
1030 Rank2: string(request.Rank2()),
1031 Rank3: string(request.Rank3()),
Filip Kujawa210a03b2022-11-24 14:41:11 -08001032 })
1033
1034 if err != nil {
1035 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert driver ranking: %v", err))
1036 return
1037 }
1038
1039 var response SubmitDriverRankingResponseT
1040 builder := flatbuffers.NewBuilder(10)
1041 builder.Finish((&response).Pack(builder))
1042 w.Write(builder.FinishedBytes())
1043}
1044
Filip Kujawaf882e022022-12-14 13:14:08 -08001045type requestAllNotesHandler struct {
1046 db Database
1047}
1048
1049func (handler requestAllNotesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
1050 requestBytes, err := io.ReadAll(req.Body)
1051 if err != nil {
1052 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
1053 return
1054 }
1055
1056 _, success := parseRequest(w, requestBytes, "RequestAllNotes", request_all_notes.GetRootAsRequestAllNotes)
1057 if !success {
1058 return
1059 }
1060
1061 notes, err := handler.db.ReturnAllNotes()
1062 if err != nil {
1063 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
1064 return
1065 }
1066
1067 var response RequestAllNotesResponseT
1068 for _, note := range notes {
1069 response.NoteList = append(response.NoteList, &request_all_notes_response.NoteT{
Filip Kujawa7ddd5652023-03-07 19:56:15 -08001070 Team: note.TeamNumber,
1071 Notes: note.Notes,
1072 GoodDriving: note.GoodDriving,
1073 BadDriving: note.BadDriving,
Filip Kujawa11dc4c92023-04-13 08:55:43 -07001074 SolidPlacing: note.SolidPlacing,
Filip Kujawa7ddd5652023-03-07 19:56:15 -08001075 SketchyPlacing: note.SketchyPlacing,
1076 GoodDefense: note.GoodDefense,
1077 BadDefense: note.BadDefense,
1078 EasilyDefended: note.EasilyDefended,
Emily Markovacf893f42024-03-13 19:03:10 -07001079 NoShow: note.NoShow,
1080 MatchNumber: note.MatchNumber,
1081 CompLevel: note.CompLevel,
1082 SetNumber: note.SetNumber,
Filip Kujawaf882e022022-12-14 13:14:08 -08001083 })
1084 }
1085
1086 builder := flatbuffers.NewBuilder(50 * 1024)
1087 builder.Finish((&response).Pack(builder))
1088 w.Write(builder.FinishedBytes())
1089}
1090
1091type requestAllDriverRankingsHandler struct {
1092 db Database
1093}
1094
1095func (handler requestAllDriverRankingsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
1096 requestBytes, err := io.ReadAll(req.Body)
1097 if err != nil {
1098 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
1099 return
1100 }
1101
1102 _, success := parseRequest(w, requestBytes, "RequestAllDriverRankings", request_all_driver_rankings.GetRootAsRequestAllDriverRankings)
1103 if !success {
1104 return
1105 }
1106
1107 rankings, err := handler.db.ReturnAllDriverRankings()
1108 if err != nil {
1109 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
1110 return
1111 }
1112
1113 var response RequestAllDriverRankingsResponseT
1114 for _, ranking := range rankings {
1115 response.DriverRankingList = append(response.DriverRankingList, &request_all_driver_rankings_response.RankingT{
1116 MatchNumber: ranking.MatchNumber,
1117 Rank1: ranking.Rank1,
1118 Rank2: ranking.Rank2,
1119 Rank3: ranking.Rank3,
1120 })
1121 }
1122
1123 builder := flatbuffers.NewBuilder(50 * 1024)
1124 builder.Finish((&response).Pack(builder))
1125 w.Write(builder.FinishedBytes())
1126}
1127
Emily Markova8cb91312024-02-02 12:30:37 -08001128type submit2024ActionsHandler struct {
1129 db Database
1130}
1131
1132func (handler submit2024ActionsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
1133 // Get the username of the person submitting the data.
1134 username := parseUsername(req)
1135
1136 requestBytes, err := io.ReadAll(req.Body)
1137 if err != nil {
1138 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
1139 return
1140 }
1141
1142 request, success := parseRequest(w, requestBytes, "Submit2024Actions", submit_2024_actions.GetRootAsSubmit2024Actions)
1143 if !success {
1144 return
1145 }
1146
Philipp Schradera6f629b2024-02-25 20:45:01 -08001147 log.Println("Got actions for match", request.MatchNumber(), "team", string(request.TeamNumber()), "from", username)
Emily Markova8cb91312024-02-02 12:30:37 -08001148
1149 for i := 0; i < request.ActionsListLength(); i++ {
1150
1151 var action Action2024
1152 request.ActionsList(&action, i)
1153
1154 dbAction := db.Action{
1155 PreScouting: request.PreScouting(),
1156 TeamNumber: string(request.TeamNumber()),
1157 MatchNumber: request.MatchNumber(),
1158 SetNumber: request.SetNumber(),
1159 CompLevel: string(request.CompLevel()),
1160 //TODO: Serialize CompletedAction
1161 CompletedAction: []byte{},
1162 Timestamp: action.Timestamp(),
1163 CollectedBy: username,
1164 }
1165
1166 // Do some error checking.
1167 if action.Timestamp() < 0 {
1168 respondWithError(w, http.StatusBadRequest, fmt.Sprint(
1169 "Invalid timestamp field value of ", action.Timestamp()))
1170 return
1171 }
1172
1173 err = handler.db.AddAction(dbAction)
1174 if err != nil {
1175 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to add action to database: ", err))
1176 return
1177 }
1178 }
1179
1180 stats, err := ConvertActionsToStat2024(request)
1181 if err != nil {
1182 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to convert actions to stats: ", err))
1183 return
1184 }
1185
1186 stats.CollectedBy = username
1187
1188 err = handler.db.AddToStats2024(stats)
1189 if err != nil {
Emily Markovadcadcb62024-02-03 13:07:17 -08001190 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to submit stats2024: ", stats, ": ", err))
Emily Markova8cb91312024-02-02 12:30:37 -08001191 return
1192 }
1193
1194 builder := flatbuffers.NewBuilder(50 * 1024)
Emily Markovadcadcb62024-02-03 13:07:17 -08001195 builder.Finish((&Submit2024ActionsResponseT{}).Pack(builder))
Emily Markova8cb91312024-02-02 12:30:37 -08001196 w.Write(builder.FinishedBytes())
1197}
1198
Sabina Leaver9b4eb312023-02-20 19:58:17 -08001199type submitActionsHandler struct {
1200 db Database
1201}
1202
1203func (handler submitActionsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
1204 // Get the username of the person submitting the data.
1205 username := parseUsername(req)
1206
1207 requestBytes, err := io.ReadAll(req.Body)
1208 if err != nil {
1209 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
1210 return
1211 }
1212
1213 request, success := parseRequest(w, requestBytes, "SubmitActions", submit_actions.GetRootAsSubmitActions)
1214 if !success {
1215 return
1216 }
1217
Philipp Schradera6f629b2024-02-25 20:45:01 -08001218 log.Println("Got actions for match", request.MatchNumber(), "team", string(request.TeamNumber()), "from", username)
Sabina Leaver9b4eb312023-02-20 19:58:17 -08001219
1220 for i := 0; i < request.ActionsListLength(); i++ {
1221
1222 var action Action
1223 request.ActionsList(&action, i)
1224
1225 dbAction := db.Action{
Philipp Schrader4b489222023-04-15 16:40:16 -07001226 PreScouting: request.PreScouting(),
Sabina Leaver9b4eb312023-02-20 19:58:17 -08001227 TeamNumber: string(request.TeamNumber()),
1228 MatchNumber: request.MatchNumber(),
1229 SetNumber: request.SetNumber(),
1230 CompLevel: string(request.CompLevel()),
1231 //TODO: Serialize CompletedAction
1232 CompletedAction: []byte{},
Philipp Schrader670a1c82023-05-17 19:42:43 -07001233 Timestamp: action.Timestamp(),
Sabina Leaver9b4eb312023-02-20 19:58:17 -08001234 CollectedBy: username,
1235 }
1236
1237 // Do some error checking.
1238 if action.Timestamp() < 0 {
1239 respondWithError(w, http.StatusBadRequest, fmt.Sprint(
1240 "Invalid timestamp field value of ", action.Timestamp()))
1241 return
1242 }
1243
1244 err = handler.db.AddAction(dbAction)
1245 if err != nil {
1246 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to add action to database: ", err))
1247 return
1248 }
1249 }
1250
Philipp Schradere11114f2023-04-15 17:04:25 -07001251 stats, err := ConvertActionsToStat(request)
1252 if err != nil {
1253 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to convert actions to stats: ", err))
1254 return
1255 }
1256
1257 stats.CollectedBy = username
1258
1259 err = handler.db.AddToStats2023(stats)
1260 if err != nil {
1261 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to submit stats: ", stats, ": ", err))
1262 return
1263 }
1264
Sabina Leaver9b4eb312023-02-20 19:58:17 -08001265 builder := flatbuffers.NewBuilder(50 * 1024)
1266 builder.Finish((&SubmitActionsResponseT{}).Pack(builder))
1267 w.Write(builder.FinishedBytes())
1268}
1269
Emily Markova8cb91312024-02-02 12:30:37 -08001270type Delete2024DataScoutingHandler struct {
1271 db Database
1272}
1273
1274func (handler Delete2024DataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
1275 requestBytes, err := io.ReadAll(req.Body)
1276 if err != nil {
1277 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
1278 return
1279 }
1280
1281 request, success := parseRequest(w, requestBytes, "Delete2024DataScouting", delete_2024_data_scouting.GetRootAsDelete2024DataScouting)
1282 if !success {
1283 return
1284 }
1285
1286 err = handler.db.DeleteFromStats2024(
1287 string(request.CompLevel()),
1288 request.MatchNumber(),
1289 request.SetNumber(),
1290 string(request.TeamNumber()))
1291
1292 if err != nil {
1293 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to delete from stats2024: %v", err))
1294 return
1295 }
1296
1297 err = handler.db.DeleteFromActions(
1298 string(request.CompLevel()),
1299 request.MatchNumber(),
1300 request.SetNumber(),
1301 string(request.TeamNumber()))
1302
1303 if err != nil {
1304 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to delete from actions: %v", err))
1305 return
1306 }
1307
1308 var response Delete2024DataScoutingResponseT
1309 builder := flatbuffers.NewBuilder(10)
1310 builder.Finish((&response).Pack(builder))
1311 w.Write(builder.FinishedBytes())
1312}
1313
Filip Kujawac1ded372023-05-27 14:33:43 -07001314type Delete2023DataScoutingHandler struct {
1315 db Database
1316}
1317
1318func (handler Delete2023DataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
1319 requestBytes, err := io.ReadAll(req.Body)
1320 if err != nil {
1321 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
1322 return
1323 }
1324
1325 request, success := parseRequest(w, requestBytes, "Delete2023DataScouting", delete_2023_data_scouting.GetRootAsDelete2023DataScouting)
1326 if !success {
1327 return
1328 }
1329
1330 err = handler.db.DeleteFromStats(
1331 string(request.CompLevel()),
1332 request.MatchNumber(),
1333 request.SetNumber(),
1334 string(request.TeamNumber()))
1335
1336 if err != nil {
1337 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to delete from stats: %v", err))
1338 return
1339 }
1340
1341 err = handler.db.DeleteFromActions(
1342 string(request.CompLevel()),
1343 request.MatchNumber(),
1344 request.SetNumber(),
1345 string(request.TeamNumber()))
1346
1347 if err != nil {
1348 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to delete from actions: %v", err))
1349 return
1350 }
1351
1352 var response Delete2023DataScoutingResponseT
1353 builder := flatbuffers.NewBuilder(10)
1354 builder.Finish((&response).Pack(builder))
1355 w.Write(builder.FinishedBytes())
1356}
1357
Philipp Schrader43c730b2023-02-26 20:27:44 -08001358func HandleRequests(db Database, scoutingServer server.ScoutingServer) {
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08001359 scoutingServer.HandleFunc("/requests", unknown)
Philipp Schradercbf5c6a2022-02-27 23:25:19 -08001360 scoutingServer.Handle("/requests/request/all_matches", requestAllMatchesHandler{db})
Filip Kujawaf882e022022-12-14 13:14:08 -08001361 scoutingServer.Handle("/requests/request/all_notes", requestAllNotesHandler{db})
1362 scoutingServer.Handle("/requests/request/all_driver_rankings", requestAllDriverRankingsHandler{db})
Emily Markova290147d2023-03-03 22:40:06 -08001363 scoutingServer.Handle("/requests/request/2023_data_scouting", request2023DataScoutingHandler{db})
Emily Markova8cb91312024-02-02 12:30:37 -08001364 scoutingServer.Handle("/requests/request/2024_data_scouting", request2024DataScoutingHandler{db})
Alex Perry81f96ba2022-03-13 18:26:19 -07001365 scoutingServer.Handle("/requests/submit/submit_notes", submitNoteScoutingHandler{db})
Emily Markovafaecfe12023-07-01 12:40:03 -07001366 scoutingServer.Handle("/requests/submit/submit_pit_image", submitPitImageScoutingHandler{db})
1367 scoutingServer.Handle("/requests/request/pit_images", requestPitImagesHandler{db})
Emily Markova8e39f452023-12-23 12:17:30 -08001368 scoutingServer.Handle("/requests/request/all_pit_images", requestAllPitImagesHandler{db})
Alex Perry81f96ba2022-03-13 18:26:19 -07001369 scoutingServer.Handle("/requests/request/notes_for_team", requestNotesForTeamHandler{db})
Milo Lin1d59f0c2022-06-22 20:30:58 -07001370 scoutingServer.Handle("/requests/submit/shift_schedule", submitShiftScheduleHandler{db})
1371 scoutingServer.Handle("/requests/request/shift_schedule", requestShiftScheduleHandler{db})
Filip Kujawa210a03b2022-11-24 14:41:11 -08001372 scoutingServer.Handle("/requests/submit/submit_driver_ranking", SubmitDriverRankingHandler{db})
Sabina Leaver9b4eb312023-02-20 19:58:17 -08001373 scoutingServer.Handle("/requests/submit/submit_actions", submitActionsHandler{db})
Emily Markova8cb91312024-02-02 12:30:37 -08001374 scoutingServer.Handle("/requests/submit/submit_2024_actions", submit2024ActionsHandler{db})
Filip Kujawac1ded372023-05-27 14:33:43 -07001375 scoutingServer.Handle("/requests/delete/delete_2023_data_scouting", Delete2023DataScoutingHandler{db})
Emily Markova8cb91312024-02-02 12:30:37 -08001376 scoutingServer.Handle("/requests/delete/delete_2024_data_scouting", Delete2024DataScoutingHandler{db})
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08001377}