[Scouting App] Add Driver Ranking

Move the driver ranking data collection from google forms to the scouting app.
The data collected is used to calculate a overall driver rank for the picklist.

Signed-off-by: Filip Kujawa <filip.j.kujawa@gmail.com>
Change-Id: Id459e8dec1fa79c1a9f49cc40ffa83014e51db16
diff --git a/scouting/webserver/requests/BUILD b/scouting/webserver/requests/BUILD
index 87575a9..5c9ede4 100644
--- a/scouting/webserver/requests/BUILD
+++ b/scouting/webserver/requests/BUILD
@@ -24,6 +24,8 @@
         "//scouting/webserver/requests/messages:request_shift_schedule_response_go_fbs",
         "//scouting/webserver/requests/messages:submit_data_scouting_go_fbs",
         "//scouting/webserver/requests/messages:submit_data_scouting_response_go_fbs",
+        "//scouting/webserver/requests/messages:submit_driver_ranking_go_fbs",
+        "//scouting/webserver/requests/messages:submit_driver_ranking_response_go_fbs",
         "//scouting/webserver/requests/messages:submit_notes_go_fbs",
         "//scouting/webserver/requests/messages:submit_notes_response_go_fbs",
         "//scouting/webserver/requests/messages:submit_shift_schedule_go_fbs",
@@ -56,6 +58,7 @@
         "//scouting/webserver/requests/messages:request_shift_schedule_response_go_fbs",
         "//scouting/webserver/requests/messages:submit_data_scouting_go_fbs",
         "//scouting/webserver/requests/messages:submit_data_scouting_response_go_fbs",
+        "//scouting/webserver/requests/messages:submit_driver_ranking_go_fbs",
         "//scouting/webserver/requests/messages:submit_notes_go_fbs",
         "//scouting/webserver/requests/messages:submit_shift_schedule_go_fbs",
         "//scouting/webserver/server",
diff --git a/scouting/webserver/requests/debug/BUILD b/scouting/webserver/requests/debug/BUILD
index 04c4ffa..f826831 100644
--- a/scouting/webserver/requests/debug/BUILD
+++ b/scouting/webserver/requests/debug/BUILD
@@ -15,6 +15,7 @@
         "//scouting/webserver/requests/messages:request_notes_for_team_response_go_fbs",
         "//scouting/webserver/requests/messages:request_shift_schedule_response_go_fbs",
         "//scouting/webserver/requests/messages:submit_data_scouting_response_go_fbs",
+        "//scouting/webserver/requests/messages:submit_driver_ranking_response_go_fbs",
         "//scouting/webserver/requests/messages:submit_notes_response_go_fbs",
         "//scouting/webserver/requests/messages:submit_shift_schedule_response_go_fbs",
         "@com_github_google_flatbuffers//go:go_default_library",
diff --git a/scouting/webserver/requests/debug/debug.go b/scouting/webserver/requests/debug/debug.go
index b3df518..fc0896c 100644
--- a/scouting/webserver/requests/debug/debug.go
+++ b/scouting/webserver/requests/debug/debug.go
@@ -17,6 +17,7 @@
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_shift_schedule_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
+	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_driver_ranking_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_shift_schedule_response"
 	flatbuffers "github.com/google/flatbuffers/go"
@@ -157,3 +158,9 @@
 		server+"/requests/submit/shift_schedule", requestBytes,
 		submit_shift_schedule_response.GetRootAsSubmitShiftScheduleResponse)
 }
+
+func SubmitDriverRanking(server string, requestBytes []byte) (*submit_driver_ranking_response.SubmitDriverRankingResponseT, error) {
+	return sendMessage[submit_driver_ranking_response.SubmitDriverRankingResponseT](
+		server+"/requests/submit/submit_driver_ranking", requestBytes,
+		submit_driver_ranking_response.GetRootAsSubmitDriverRankingResponse)
+}
diff --git a/scouting/webserver/requests/messages/BUILD b/scouting/webserver/requests/messages/BUILD
index b2d21a2..c14a857 100644
--- a/scouting/webserver/requests/messages/BUILD
+++ b/scouting/webserver/requests/messages/BUILD
@@ -21,6 +21,8 @@
     "request_shift_schedule_response",
     "submit_shift_schedule",
     "submit_shift_schedule_response",
+    "submit_driver_ranking",
+    "submit_driver_ranking_response",
 )
 
 filegroup(
diff --git a/scouting/webserver/requests/messages/submit_driver_ranking.fbs b/scouting/webserver/requests/messages/submit_driver_ranking.fbs
new file mode 100644
index 0000000..ac1e218
--- /dev/null
+++ b/scouting/webserver/requests/messages/submit_driver_ranking.fbs
@@ -0,0 +1,10 @@
+namespace scouting.webserver.requests;
+
+table SubmitDriverRanking {
+    matchNumber:int (id: 0);
+    rank1:int (id: 1);
+    rank2:int (id: 2);
+    rank3:int (id: 3);
+}
+
+root_type SubmitDriverRanking;
diff --git a/scouting/webserver/requests/messages/submit_driver_ranking_response.fbs b/scouting/webserver/requests/messages/submit_driver_ranking_response.fbs
new file mode 100644
index 0000000..78c6445
--- /dev/null
+++ b/scouting/webserver/requests/messages/submit_driver_ranking_response.fbs
@@ -0,0 +1,8 @@
+namespace scouting.webserver.requests;
+
+table SubmitDriverRankingResponse {
+    // empty response
+}
+
+root_type SubmitDriverRankingResponse;
+
diff --git a/scouting/webserver/requests/requests.go b/scouting/webserver/requests/requests.go
index e33e82d..12bc3da 100644
--- a/scouting/webserver/requests/requests.go
+++ b/scouting/webserver/requests/requests.go
@@ -27,6 +27,8 @@
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_shift_schedule_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
+	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_driver_ranking"
+	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_driver_ranking_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_shift_schedule"
@@ -53,6 +55,8 @@
 type RequestShiftScheduleResponseT = request_shift_schedule_response.RequestShiftScheduleResponseT
 type SubmitShiftSchedule = submit_shift_schedule.SubmitShiftSchedule
 type SubmitShiftScheduleResponseT = submit_shift_schedule_response.SubmitShiftScheduleResponseT
+type SubmitDriverRanking = submit_driver_ranking.SubmitDriverRanking
+type SubmitDriverRankingResponseT = submit_driver_ranking_response.SubmitDriverRankingResponseT
 
 // The interface we expect the database abstraction to conform to.
 // We use an interface here because it makes unit testing easier.
@@ -68,6 +72,7 @@
 	QueryStats(int) ([]db.Stats, error)
 	QueryNotes(int32) ([]string, error)
 	AddNotes(db.NotesData) error
+	AddDriverRanking(db.DriverRankingData) error
 }
 
 type ScrapeMatchList func(int32, string) ([]scraping.Match, error)
@@ -608,6 +613,40 @@
 	w.Write(builder.FinishedBytes())
 }
 
+type SubmitDriverRankingHandler struct {
+	db Database
+}
+
+func (handler SubmitDriverRankingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	requestBytes, err := io.ReadAll(req.Body)
+	if err != nil {
+		respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
+		return
+	}
+
+	request, success := parseRequest(w, requestBytes, "SubmitDriverRanking", submit_driver_ranking.GetRootAsSubmitDriverRanking)
+	if !success {
+		return
+	}
+
+	err = handler.db.AddDriverRanking(db.DriverRankingData{
+		MatchNumber: request.MatchNumber(),
+		Rank1:       request.Rank1(),
+		Rank2:       request.Rank2(),
+		Rank3:       request.Rank3(),
+	})
+
+	if err != nil {
+		respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert driver ranking: %v", err))
+		return
+	}
+
+	var response SubmitDriverRankingResponseT
+	builder := flatbuffers.NewBuilder(10)
+	builder.Finish((&response).Pack(builder))
+	w.Write(builder.FinishedBytes())
+}
+
 func HandleRequests(db Database, scrape ScrapeMatchList, scoutingServer server.ScoutingServer) {
 	scoutingServer.HandleFunc("/requests", unknown)
 	scoutingServer.Handle("/requests/submit/data_scouting", submitDataScoutingHandler{db})
@@ -619,4 +658,5 @@
 	scoutingServer.Handle("/requests/request/notes_for_team", requestNotesForTeamHandler{db})
 	scoutingServer.Handle("/requests/submit/shift_schedule", submitShiftScheduleHandler{db})
 	scoutingServer.Handle("/requests/request/shift_schedule", requestShiftScheduleHandler{db})
+	scoutingServer.Handle("/requests/submit/submit_driver_ranking", SubmitDriverRankingHandler{db})
 }
diff --git a/scouting/webserver/requests/requests_test.go b/scouting/webserver/requests/requests_test.go
index 85ab916..55b789b 100644
--- a/scouting/webserver/requests/requests_test.go
+++ b/scouting/webserver/requests/requests_test.go
@@ -24,6 +24,7 @@
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_shift_schedule_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
+	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_driver_ranking"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_shift_schedule"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/server"
@@ -560,14 +561,44 @@
 	}
 }
 
+func TestSubmitDriverRanking(t *testing.T) {
+	database := MockDatabase{}
+	scoutingServer := server.NewScoutingServer()
+	HandleRequests(&database, scrapeEmtpyMatchList, scoutingServer)
+	scoutingServer.Start(8080)
+	defer scoutingServer.Stop()
+
+	builder := flatbuffers.NewBuilder(1024)
+	builder.Finish((&submit_driver_ranking.SubmitDriverRankingT{
+		MatchNumber: 36,
+		Rank1:       1234,
+		Rank2:       1235,
+		Rank3:       1236,
+	}).Pack(builder))
+
+	_, err := debug.SubmitDriverRanking("http://localhost:8080", builder.FinishedBytes())
+	if err != nil {
+		t.Fatal("Failed to submit driver ranking: ", err)
+	}
+
+	expected := []db.DriverRankingData{
+		{MatchNumber: 36, Rank1: 1234, Rank2: 1235, Rank3: 1236},
+	}
+
+	if !reflect.DeepEqual(database.driver_ranking, expected) {
+		t.Fatal("Submitted notes did not match", expected, database.notes)
+	}
+}
+
 // A mocked database we can use for testing. Add functionality to this as
 // needed for your tests.
 
 type MockDatabase struct {
-	matches       []db.Match
-	stats         []db.Stats
-	notes         []db.NotesData
-	shiftSchedule []db.Shift
+	matches        []db.Match
+	stats          []db.Stats
+	notes          []db.NotesData
+	shiftSchedule  []db.Shift
+	driver_ranking []db.DriverRankingData
 }
 
 func (database *MockDatabase) AddToMatch(match db.Match) error {
@@ -633,6 +664,11 @@
 	return []db.Shift{}, nil
 }
 
+func (database *MockDatabase) AddDriverRanking(data db.DriverRankingData) error {
+	database.driver_ranking = append(database.driver_ranking, data)
+	return nil
+}
+
 // Returns an empty match list from the fake The Blue Alliance scraping.
 func scrapeEmtpyMatchList(int32, string) ([]scraping.Match, error) {
 	return nil, nil