scouting: Add support for /requests/request/data_scouting
Signed-off-by: Philipp Schrader <philipp.schrader@gmail.com>
Change-Id: I6a53ed395a187f04c889af506ce6ecacd0a75916
diff --git a/BUILD b/BUILD
index 232ac67..d2113f2 100644
--- a/BUILD
+++ b/BUILD
@@ -15,7 +15,7 @@
# gazelle:resolve go github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response //scouting/webserver/requests/messages:error_response_go_fbs
# gazelle:resolve go github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting //scouting/webserver/requests/messages:submit_data_scouting_go_fbs
# gazelle:resolve go github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response //scouting/webserver/requests/messages:submit_data_scouting_response_go_fbs
-# gazelle:resolve go github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting_response //scouting/webserver/requests/messages:query_data_scouting_response_go_fbs
+# gazelle:resolve go github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting_response //scouting/webserver/requests/messages:request_data_scouting_response_go_fbs
# gazelle:resolve go github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting //scouting/webserver/requests/messages:request_data_scouting_go_fbs
# gazelle:resolve go github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team_response //scouting/webserver/requests/messages:request_matches_for_team_response_go_fbs
# gazelle:resolve go github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team //scouting/webserver/requests/messages:request_matches_for_team_go_fbs
diff --git a/scouting/webserver/requests/BUILD b/scouting/webserver/requests/BUILD
index 2d363fd..196c522 100644
--- a/scouting/webserver/requests/BUILD
+++ b/scouting/webserver/requests/BUILD
@@ -11,6 +11,8 @@
"//scouting/webserver/requests/messages:error_response_go_fbs",
"//scouting/webserver/requests/messages:request_all_matches_go_fbs",
"//scouting/webserver/requests/messages:request_all_matches_response_go_fbs",
+ "//scouting/webserver/requests/messages:request_data_scouting_go_fbs",
+ "//scouting/webserver/requests/messages:request_data_scouting_response_go_fbs",
"//scouting/webserver/requests/messages:request_matches_for_team_go_fbs",
"//scouting/webserver/requests/messages:request_matches_for_team_response_go_fbs",
"//scouting/webserver/requests/messages:submit_data_scouting_go_fbs",
@@ -31,6 +33,8 @@
"//scouting/webserver/requests/messages:error_response_go_fbs",
"//scouting/webserver/requests/messages:request_all_matches_go_fbs",
"//scouting/webserver/requests/messages:request_all_matches_response_go_fbs",
+ "//scouting/webserver/requests/messages:request_data_scouting_go_fbs",
+ "//scouting/webserver/requests/messages:request_data_scouting_response_go_fbs",
"//scouting/webserver/requests/messages:request_matches_for_team_go_fbs",
"//scouting/webserver/requests/messages:request_matches_for_team_response_go_fbs",
"//scouting/webserver/requests/messages:submit_data_scouting_go_fbs",
diff --git a/scouting/webserver/requests/debug/BUILD b/scouting/webserver/requests/debug/BUILD
index d56c03e..e3028dc 100644
--- a/scouting/webserver/requests/debug/BUILD
+++ b/scouting/webserver/requests/debug/BUILD
@@ -9,6 +9,7 @@
deps = [
"//scouting/webserver/requests/messages:error_response_go_fbs",
"//scouting/webserver/requests/messages:request_all_matches_response_go_fbs",
+ "//scouting/webserver/requests/messages:request_data_scouting_response_go_fbs",
"//scouting/webserver/requests/messages:request_matches_for_team_response_go_fbs",
"//scouting/webserver/requests/messages:submit_data_scouting_response_go_fbs",
],
diff --git a/scouting/webserver/requests/debug/cli/cli_test.py b/scouting/webserver/requests/debug/cli/cli_test.py
index 6b47ae9..347c828 100644
--- a/scouting/webserver/requests/debug/cli/cli_test.py
+++ b/scouting/webserver/requests/debug/cli/cli_test.py
@@ -88,5 +88,13 @@
self.assertEqual(exit_code, 0)
self.assertIn("{MatchList:[]}", stderr)
+ def test_request_data_scouting(self):
+ json_path = write_json({})
+ exit_code, _stdout, stderr = run_debug_cli(["-requestDataScouting", json_path])
+
+ # TODO(phil): Actually add data here before querying it.
+ self.assertEqual(exit_code, 0)
+ self.assertIn("{StatsList:[]}", stderr)
+
if __name__ == "__main__":
unittest.main()
diff --git a/scouting/webserver/requests/debug/cli/main.go b/scouting/webserver/requests/debug/cli/main.go
index 44ae761..0782d82 100644
--- a/scouting/webserver/requests/debug/cli/main.go
+++ b/scouting/webserver/requests/debug/cli/main.go
@@ -74,6 +74,8 @@
"If specified, parse the file as a RequestAllMatches JSON request.")
requestMatchesForTeamPtr := flag.String("requestMatchesForTeam", "",
"If specified, parse the file as a RequestMatchesForTeam JSON request.")
+ requestDataScoutingPtr := flag.String("requestDataScouting", "",
+ "If specified, parse the file as a RequestDataScouting JSON request.")
flag.Parse()
// Handle the actual arguments.
@@ -110,4 +112,15 @@
}
log.Printf("%+v", *response)
}
+ if *requestDataScoutingPtr != "" {
+ log.Printf("Sending RequestDataScouting to %s", *addressPtr)
+ binaryRequest := parseJson(
+ "scouting/webserver/requests/messages/request_data_scouting.fbs",
+ *requestDataScoutingPtr)
+ response, err := debug.RequestDataScouting(*addressPtr, binaryRequest)
+ if err != nil {
+ log.Fatal("Failed RequestDataScouting: ", err)
+ }
+ log.Printf("%+v", *response)
+ }
}
diff --git a/scouting/webserver/requests/debug/debug.go b/scouting/webserver/requests/debug/debug.go
index 6add662..6515e81 100644
--- a/scouting/webserver/requests/debug/debug.go
+++ b/scouting/webserver/requests/debug/debug.go
@@ -10,6 +10,7 @@
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches_response"
+ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
)
@@ -18,6 +19,7 @@
type SubmitDataScoutingResponseT = submit_data_scouting_response.SubmitDataScoutingResponseT
type RequestAllMatchesResponseT = request_all_matches_response.RequestAllMatchesResponseT
type RequestMatchesForTeamResponseT = request_matches_for_team_response.RequestMatchesForTeamResponseT
+type RequestDataScoutingResponseT = request_data_scouting_response.RequestDataScoutingResponseT
// A struct that can be used as an `error`. It contains information about the
// why the server was unhappy and what the corresponding request was.
@@ -113,3 +115,15 @@
response := request_matches_for_team_response.GetRootAsRequestMatchesForTeamResponse(responseBytes, 0)
return response.UnPack(), nil
}
+
+// Sends a `RequestDataScouting` message to the server and returns the
+// deserialized response.
+func RequestDataScouting(server string, requestBytes []byte) (*RequestDataScoutingResponseT, error) {
+ responseBytes, err := performPost(server+"/requests/request/data_scouting", requestBytes)
+ if err != nil {
+ return nil, err
+ }
+ log.Printf("Parsing RequestDataScoutingResponse")
+ response := request_data_scouting_response.GetRootAsRequestDataScoutingResponse(responseBytes, 0)
+ return response.UnPack(), nil
+}
diff --git a/scouting/webserver/requests/messages/request_data_scouting.fbs b/scouting/webserver/requests/messages/request_data_scouting.fbs
index 45f6308..884c1a6 100644
--- a/scouting/webserver/requests/messages/request_data_scouting.fbs
+++ b/scouting/webserver/requests/messages/request_data_scouting.fbs
@@ -1,7 +1,7 @@
namespace scouting.webserver.requests;
-table QueryDataScouting {
+table RequestDataScouting {
// TODO: Implement this.
}
-root_type QueryDataScouting;
\ No newline at end of file
+root_type RequestDataScouting;
diff --git a/scouting/webserver/requests/messages/request_data_scouting_response.fbs b/scouting/webserver/requests/messages/request_data_scouting_response.fbs
index 83f5cd0..e30ab42 100644
--- a/scouting/webserver/requests/messages/request_data_scouting_response.fbs
+++ b/scouting/webserver/requests/messages/request_data_scouting_response.fbs
@@ -1,6 +1,6 @@
namespace scouting.webserver.requests;
-struct Stats {
+table Stats {
team:int (id: 0);
match:int (id: 1);
missed_shots_auto:int (id: 2);
@@ -13,8 +13,8 @@
climbing:int (id:9);
}
-table QueryDataScoutingResponse {
+table RequestDataScoutingResponse {
stats_list:[Stats] (id:0);
}
-root_type QueryDataScoutingResponse;
\ No newline at end of file
+root_type RequestDataScoutingResponse;
diff --git a/scouting/webserver/requests/requests.go b/scouting/webserver/requests/requests.go
index 44c0b60..3c21402 100644
--- a/scouting/webserver/requests/requests.go
+++ b/scouting/webserver/requests/requests.go
@@ -9,6 +9,8 @@
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches_response"
+ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting"
+ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting"
@@ -22,6 +24,8 @@
type RequestAllMatchesResponseT = request_all_matches_response.RequestAllMatchesResponseT
type RequestMatchesForTeam = request_matches_for_team.RequestMatchesForTeam
type RequestMatchesForTeamResponseT = request_matches_for_team_response.RequestMatchesForTeamResponseT
+type RequestDataScouting = request_data_scouting.RequestDataScouting
+type RequestDataScoutingResponseT = request_data_scouting_response.RequestDataScoutingResponseT
// The interface we expect the database abstraction to conform to.
// We use an interface here because it makes unit testing easier.
@@ -199,9 +203,66 @@
w.Write(builder.FinishedBytes())
}
+// TODO(phil): Can we turn this into a generic?
+func parseRequestDataScouting(w http.ResponseWriter, buf []byte) (*RequestDataScouting, bool) {
+ success := true
+ defer func() {
+ if r := recover(); r != nil {
+ respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
+ success = false
+ }
+ }()
+ result := request_data_scouting.GetRootAsRequestDataScouting(buf, 0)
+ return result, success
+}
+
+// Handles a RequestDataScouting request.
+type requestDataScoutingHandler struct {
+ db Database
+}
+
+func (handler requestDataScoutingHandler) 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
+ }
+
+ _, success := parseRequestDataScouting(w, requestBytes)
+ if !success {
+ return
+ }
+
+ stats, err := handler.db.ReturnStats()
+ if err != nil {
+ respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Faled to query database: ", err))
+ }
+
+ var response RequestDataScoutingResponseT
+ for _, stat := range stats {
+ response.StatsList = append(response.StatsList, &request_data_scouting_response.StatsT{
+ Team: stat.TeamNumber,
+ Match: stat.MatchNumber,
+ MissedShotsAuto: stat.ShotsMissedAuto,
+ UpperGoalAuto: stat.UpperGoalAuto,
+ LowerGoalAuto: stat.LowerGoalAuto,
+ MissedShotsTele: stat.ShotsMissed,
+ UpperGoalTele: stat.UpperGoalShots,
+ LowerGoalTele: stat.LowerGoalShots,
+ DefenseRating: stat.PlayedDefense,
+ Climbing: stat.Climbing,
+ })
+ }
+
+ builder := flatbuffers.NewBuilder(50 * 1024)
+ builder.Finish((&response).Pack(builder))
+ w.Write(builder.FinishedBytes())
+}
+
func HandleRequests(db Database, scoutingServer server.ScoutingServer) {
scoutingServer.HandleFunc("/requests", unknown)
scoutingServer.Handle("/requests/submit/data_scouting", submitDataScoutingHandler{db})
scoutingServer.Handle("/requests/request/all_matches", requestAllMatchesHandler{db})
scoutingServer.Handle("/requests/request/matches_for_team", requestMatchesForTeamHandler{db})
+ scoutingServer.Handle("/requests/request/data_scouting", requestDataScoutingHandler{db})
}
diff --git a/scouting/webserver/requests/requests_test.go b/scouting/webserver/requests/requests_test.go
index f6a06b9..e3650ff 100644
--- a/scouting/webserver/requests/requests_test.go
+++ b/scouting/webserver/requests/requests_test.go
@@ -12,6 +12,8 @@
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches_response"
+ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting"
+ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting"
@@ -206,11 +208,73 @@
}
}
+// Validates that we can request the stats.
+func TestRequestDataScouting(t *testing.T) {
+ db := MockDatabase{
+ stats: []db.Stats{
+ {
+ TeamNumber: 971, MatchNumber: 1,
+ ShotsMissed: 1, UpperGoalShots: 2, LowerGoalShots: 3,
+ ShotsMissedAuto: 4, UpperGoalAuto: 5, LowerGoalAuto: 6,
+ PlayedDefense: 7, Climbing: 8,
+ },
+ {
+ TeamNumber: 972, MatchNumber: 1,
+ ShotsMissed: 2, UpperGoalShots: 3, LowerGoalShots: 4,
+ ShotsMissedAuto: 5, UpperGoalAuto: 6, LowerGoalAuto: 7,
+ PlayedDefense: 8, Climbing: 9,
+ },
+ },
+ }
+ scoutingServer := server.NewScoutingServer()
+ HandleRequests(&db, scoutingServer)
+ scoutingServer.Start(8080)
+ defer scoutingServer.Stop()
+
+ builder := flatbuffers.NewBuilder(1024)
+ builder.Finish((&request_data_scouting.RequestDataScoutingT{}).Pack(builder))
+
+ response, err := debug.RequestDataScouting("http://localhost:8080", builder.FinishedBytes())
+ if err != nil {
+ t.Fatal("Failed to request all matches: ", err)
+ }
+
+ expected := request_data_scouting_response.RequestDataScoutingResponseT{
+ StatsList: []*request_data_scouting_response.StatsT{
+ // Team, Match,
+ // MissedShotsAuto, UpperGoalAuto, LowerGoalAuto,
+ // MissedShotsTele, UpperGoalTele, LowerGoalTele,
+ // DefenseRating, Climbing,
+ {
+ 971, 1,
+ 4, 5, 6,
+ 1, 2, 3,
+ 7, 8,
+ },
+ {
+ 972, 1,
+ 5, 6, 7,
+ 2, 3, 4,
+ 8, 9,
+ },
+ },
+ }
+ if len(expected.StatsList) != len(response.StatsList) {
+ t.Fatal("Expected ", expected, ", but got ", *response)
+ }
+ for i, match := range expected.StatsList {
+ if !reflect.DeepEqual(*match, *response.StatsList[i]) {
+ t.Fatal("Expected for stats", i, ":", *match, ", but got:", *response.StatsList[i])
+ }
+ }
+}
+
// 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
}
func (database *MockDatabase) AddToMatch(db.Match) error {
@@ -226,7 +290,7 @@
}
func (database *MockDatabase) ReturnStats() ([]db.Stats, error) {
- return []db.Stats{}, nil
+ return database.stats, nil
}
func (database *MockDatabase) QueryMatches(requestedTeam int32) ([]db.Match, error) {