Add delete functionality to the scouting app
This patch adds the ability to delete data scouting entries
from the database through the view tab.
Signed-off-by: Filip Kujawa <filip.j.kujawa@gmail.com>
Change-Id: I294fd3ebfa3721dace00582ba7f22e3da5f0f419
diff --git a/scouting/webserver/requests/BUILD b/scouting/webserver/requests/BUILD
index 2ff8b85..795d0ee 100644
--- a/scouting/webserver/requests/BUILD
+++ b/scouting/webserver/requests/BUILD
@@ -8,6 +8,8 @@
visibility = ["//visibility:public"],
deps = [
"//scouting/db",
+ "//scouting/webserver/requests/messages:delete_2023_data_scouting_go_fbs",
+ "//scouting/webserver/requests/messages:delete_2023_data_scouting_response_go_fbs",
"//scouting/webserver/requests/messages:error_response_go_fbs",
"//scouting/webserver/requests/messages:request_2023_data_scouting_go_fbs",
"//scouting/webserver/requests/messages:request_2023_data_scouting_response_go_fbs",
@@ -42,6 +44,7 @@
deps = [
"//scouting/db",
"//scouting/webserver/requests/debug",
+ "//scouting/webserver/requests/messages:delete_2023_data_scouting_go_fbs",
"//scouting/webserver/requests/messages:request_2023_data_scouting_go_fbs",
"//scouting/webserver/requests/messages:request_2023_data_scouting_response_go_fbs",
"//scouting/webserver/requests/messages:request_all_driver_rankings_go_fbs",
diff --git a/scouting/webserver/requests/debug/BUILD b/scouting/webserver/requests/debug/BUILD
index d9bb030..ef14e5a 100644
--- a/scouting/webserver/requests/debug/BUILD
+++ b/scouting/webserver/requests/debug/BUILD
@@ -7,6 +7,7 @@
target_compatible_with = ["@platforms//cpu:x86_64"],
visibility = ["//visibility:public"],
deps = [
+ "//scouting/webserver/requests/messages:delete_2023_data_scouting_response_go_fbs",
"//scouting/webserver/requests/messages:error_response_go_fbs",
"//scouting/webserver/requests/messages:request_2023_data_scouting_response_go_fbs",
"//scouting/webserver/requests/messages:request_all_driver_rankings_response_go_fbs",
diff --git a/scouting/webserver/requests/debug/debug.go b/scouting/webserver/requests/debug/debug.go
index acb9dd4..4837cfe 100644
--- a/scouting/webserver/requests/debug/debug.go
+++ b/scouting/webserver/requests/debug/debug.go
@@ -9,6 +9,7 @@
"log"
"net/http"
+ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/delete_2023_data_scouting_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_2023_data_scouting_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_driver_rankings_response"
@@ -164,3 +165,9 @@
server+"/requests/submit/submit_actions", requestBytes,
submit_actions_response.GetRootAsSubmitActionsResponse)
}
+
+func Delete2023DataScouting(server string, requestBytes []byte) (*delete_2023_data_scouting_response.Delete2023DataScoutingResponseT, error) {
+ return sendMessage[delete_2023_data_scouting_response.Delete2023DataScoutingResponseT](
+ server+"/requests/delete/delete_2023_data_scouting", requestBytes,
+ delete_2023_data_scouting_response.GetRootAsDelete2023DataScoutingResponse)
+}
diff --git a/scouting/webserver/requests/messages/BUILD b/scouting/webserver/requests/messages/BUILD
index c1cd999..db422ed 100644
--- a/scouting/webserver/requests/messages/BUILD
+++ b/scouting/webserver/requests/messages/BUILD
@@ -23,6 +23,8 @@
"submit_driver_ranking_response",
"submit_actions",
"submit_actions_response",
+ "delete_2023_data_scouting",
+ "delete_2023_data_scouting_response",
)
filegroup(
diff --git a/scouting/webserver/requests/messages/delete_2023_data_scouting.fbs b/scouting/webserver/requests/messages/delete_2023_data_scouting.fbs
new file mode 100644
index 0000000..a2a3ce6
--- /dev/null
+++ b/scouting/webserver/requests/messages/delete_2023_data_scouting.fbs
@@ -0,0 +1,10 @@
+namespace scouting.webserver.requests;
+
+table Delete2023DataScouting {
+ comp_level:string (id: 0);
+ match_number:int (id: 1);
+ set_number:int (id: 2);
+ team_number:string (id: 3);
+}
+
+root_type Delete2023DataScouting;
diff --git a/scouting/webserver/requests/messages/delete_2023_data_scouting_response.fbs b/scouting/webserver/requests/messages/delete_2023_data_scouting_response.fbs
new file mode 100644
index 0000000..fd07526
--- /dev/null
+++ b/scouting/webserver/requests/messages/delete_2023_data_scouting_response.fbs
@@ -0,0 +1,7 @@
+namespace scouting.webserver.requests;
+
+table Delete2023DataScoutingResponse {
+ // empty response
+}
+
+root_type Delete2023DataScoutingResponse;
diff --git a/scouting/webserver/requests/requests.go b/scouting/webserver/requests/requests.go
index 37a6ff2..533959c 100644
--- a/scouting/webserver/requests/requests.go
+++ b/scouting/webserver/requests/requests.go
@@ -12,6 +12,8 @@
"strings"
"github.com/frc971/971-Robot-Code/scouting/db"
+ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/delete_2023_data_scouting"
+ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/delete_2023_data_scouting_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_2023_data_scouting"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_2023_data_scouting_response"
@@ -58,6 +60,8 @@
type SubmitActions = submit_actions.SubmitActions
type Action = submit_actions.Action
type SubmitActionsResponseT = submit_actions_response.SubmitActionsResponseT
+type Delete2023DataScouting = delete_2023_data_scouting.Delete2023DataScouting
+type Delete2023DataScoutingResponseT = delete_2023_data_scouting_response.Delete2023DataScoutingResponseT
// The interface we expect the database abstraction to conform to.
// We use an interface here because it makes unit testing easier.
@@ -76,6 +80,8 @@
AddNotes(db.NotesData) error
AddDriverRanking(db.DriverRankingData) error
AddAction(db.Action) error
+ DeleteFromStats(string, int32, int32, string) error
+ DeleteFromActions(string, int32, int32, string) error
}
// Handles unknown requests. Just returns a 404.
@@ -871,6 +877,50 @@
w.Write(builder.FinishedBytes())
}
+type Delete2023DataScoutingHandler struct {
+ db Database
+}
+
+func (handler Delete2023DataScoutingHandler) 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, "Delete2023DataScouting", delete_2023_data_scouting.GetRootAsDelete2023DataScouting)
+ if !success {
+ return
+ }
+
+ err = handler.db.DeleteFromStats(
+ string(request.CompLevel()),
+ request.MatchNumber(),
+ request.SetNumber(),
+ string(request.TeamNumber()))
+
+ if err != nil {
+ respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to delete from stats: %v", err))
+ return
+ }
+
+ err = handler.db.DeleteFromActions(
+ string(request.CompLevel()),
+ request.MatchNumber(),
+ request.SetNumber(),
+ string(request.TeamNumber()))
+
+ if err != nil {
+ respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to delete from actions: %v", err))
+ return
+ }
+
+ var response Delete2023DataScoutingResponseT
+ builder := flatbuffers.NewBuilder(10)
+ builder.Finish((&response).Pack(builder))
+ w.Write(builder.FinishedBytes())
+}
+
func HandleRequests(db Database, scoutingServer server.ScoutingServer) {
scoutingServer.HandleFunc("/requests", unknown)
scoutingServer.Handle("/requests/request/all_matches", requestAllMatchesHandler{db})
@@ -883,4 +933,5 @@
scoutingServer.Handle("/requests/request/shift_schedule", requestShiftScheduleHandler{db})
scoutingServer.Handle("/requests/submit/submit_driver_ranking", SubmitDriverRankingHandler{db})
scoutingServer.Handle("/requests/submit/submit_actions", submitActionsHandler{db})
+ scoutingServer.Handle("/requests/delete/delete_2023_data_scouting", Delete2023DataScoutingHandler{db})
}
diff --git a/scouting/webserver/requests/requests_test.go b/scouting/webserver/requests/requests_test.go
index c1fe860..20a63ca 100644
--- a/scouting/webserver/requests/requests_test.go
+++ b/scouting/webserver/requests/requests_test.go
@@ -7,6 +7,7 @@
"github.com/frc971/971-Robot-Code/scouting/db"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/debug"
+ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/delete_2023_data_scouting"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_2023_data_scouting"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_2023_data_scouting_response"
"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_driver_rankings"
@@ -907,6 +908,112 @@
}
}
+// Validates that we can delete stats.
+func TestDeleteFromStats(t *testing.T) {
+ database := MockDatabase{
+ stats2023: []db.Stats2023{
+ {
+ TeamNumber: "3634", MatchNumber: 1, SetNumber: 2,
+ CompLevel: "quals", StartingQuadrant: 3, LowCubesAuto: 10,
+ MiddleCubesAuto: 1, HighCubesAuto: 1, CubesDroppedAuto: 0,
+ LowConesAuto: 1, MiddleConesAuto: 2, HighConesAuto: 1,
+ ConesDroppedAuto: 0, LowCubes: 1, MiddleCubes: 1,
+ HighCubes: 2, CubesDropped: 1, LowCones: 1,
+ MiddleCones: 2, HighCones: 0, ConesDropped: 1, SuperchargedPieces: 0,
+ AvgCycle: 34, Mobility: false, DockedAuto: true, EngagedAuto: false,
+ BalanceAttemptAuto: false, Docked: false, Engaged: false,
+ BalanceAttempt: true, CollectedBy: "isaac",
+ },
+ {
+ TeamNumber: "2343", MatchNumber: 1, SetNumber: 2,
+ CompLevel: "quals", StartingQuadrant: 1, LowCubesAuto: 0,
+ MiddleCubesAuto: 1, HighCubesAuto: 1, CubesDroppedAuto: 2,
+ LowConesAuto: 0, MiddleConesAuto: 0, HighConesAuto: 0,
+ ConesDroppedAuto: 1, LowCubes: 0, MiddleCubes: 0,
+ HighCubes: 1, CubesDropped: 0, LowCones: 0,
+ MiddleCones: 2, HighCones: 1, ConesDropped: 1, SuperchargedPieces: 0,
+ AvgCycle: 53, Mobility: false, DockedAuto: false, EngagedAuto: false,
+ BalanceAttemptAuto: true, Docked: false, Engaged: false,
+ BalanceAttempt: true, CollectedBy: "unknown",
+ },
+ },
+ actions: []db.Action{
+ {
+ PreScouting: true,
+ TeamNumber: "3634",
+ MatchNumber: 1,
+ SetNumber: 2,
+ CompLevel: "quals",
+ CollectedBy: "debug_cli",
+ CompletedAction: []byte{},
+ Timestamp: 2400,
+ },
+ {
+ PreScouting: true,
+ TeamNumber: "2343",
+ MatchNumber: 1,
+ SetNumber: 2,
+ CompLevel: "quals",
+ CollectedBy: "debug_cli",
+ CompletedAction: []byte{},
+ Timestamp: 1009,
+ },
+ },
+ }
+ scoutingServer := server.NewScoutingServer()
+ HandleRequests(&database, scoutingServer)
+ scoutingServer.Start(8080)
+ defer scoutingServer.Stop()
+
+ builder := flatbuffers.NewBuilder(1024)
+ builder.Finish((&delete_2023_data_scouting.Delete2023DataScoutingT{
+ CompLevel: "quals",
+ MatchNumber: 1,
+ SetNumber: 2,
+ TeamNumber: "2343",
+ }).Pack(builder))
+
+ _, err := debug.Delete2023DataScouting("http://localhost:8080", builder.FinishedBytes())
+ if err != nil {
+ t.Fatal("Failed to delete from data scouting ", err)
+ }
+
+ expectedActions := []db.Action{
+ {
+ PreScouting: true,
+ TeamNumber: "3634",
+ MatchNumber: 1,
+ SetNumber: 2,
+ CompLevel: "quals",
+ CollectedBy: "debug_cli",
+ CompletedAction: []byte{},
+ Timestamp: 2400,
+ },
+ }
+
+ expectedStats := []db.Stats2023{
+ {
+ TeamNumber: "3634", MatchNumber: 1, SetNumber: 2,
+ CompLevel: "quals", StartingQuadrant: 3, LowCubesAuto: 10,
+ MiddleCubesAuto: 1, HighCubesAuto: 1, CubesDroppedAuto: 0,
+ LowConesAuto: 1, MiddleConesAuto: 2, HighConesAuto: 1,
+ ConesDroppedAuto: 0, LowCubes: 1, MiddleCubes: 1,
+ HighCubes: 2, CubesDropped: 1, LowCones: 1,
+ MiddleCones: 2, HighCones: 0, ConesDropped: 1, SuperchargedPieces: 0,
+ AvgCycle: 34, Mobility: false, DockedAuto: true, EngagedAuto: false,
+ BalanceAttemptAuto: false, Docked: false, Engaged: false,
+ BalanceAttempt: true, CollectedBy: "isaac",
+ },
+ }
+
+ if !reflect.DeepEqual(expectedActions, database.actions) {
+ t.Fatal("Expected ", expectedActions, ", but got:", database.actions)
+ }
+ if !reflect.DeepEqual(expectedStats, database.stats2023) {
+ t.Fatal("Expected ", expectedStats, ", but got:", database.stats2023)
+ }
+}
+
// A mocked database we can use for testing. Add functionality to this as
// needed for your tests.
@@ -995,3 +1102,29 @@
func (database *MockDatabase) ReturnActions() ([]db.Action, error) {
return database.actions, nil
}
+
+func (database *MockDatabase) DeleteFromStats(compLevel_ string, matchNumber_ int32, setNumber_ int32, teamNumber_ string) error {
+ for i, stat := range database.stats2023 {
+ if stat.CompLevel == compLevel_ &&
+ stat.MatchNumber == matchNumber_ &&
+ stat.SetNumber == setNumber_ &&
+ stat.TeamNumber == teamNumber_ {
+ // Match found, remove the element from the array.
+ database.stats2023 = append(database.stats2023[:i], database.stats2023[i+1:]...)
+ }
+ }
+ return nil
+}
+
+func (database *MockDatabase) DeleteFromActions(compLevel_ string, matchNumber_ int32, setNumber_ int32, teamNumber_ string) error {
+ for i, action := range database.actions {
+ if action.CompLevel == compLevel_ &&
+ action.MatchNumber == matchNumber_ &&
+ action.SetNumber == setNumber_ &&
+ action.TeamNumber == teamNumber_ {
+ // Match found, remove the element from the array.
+ database.actions = append(database.actions[:i], database.actions[i+1:]...)
+ }
+ }
+ return nil
+}