scouting: Let web page know which matches have been scouted
One of the big complaints is that the match list on the scouting app
is annoying to scroll through as the day goes on. One approach for
addressing this is to hide the matches that have already been scouted.
A simple solution for this is to enhance the request for the match
list with some booleans. Each boolean says whether that particular
team has already been scouted.
Change-Id: I3801958edf09d2edca01dbb8d54fc12cb65e52c4
Signed-off-by: Philipp Schrader <philipp.schrader@gmail.com>
diff --git a/scouting/webserver/requests/messages/request_all_matches_response.fbs b/scouting/webserver/requests/messages/request_all_matches_response.fbs
index 9d3be62..9404675 100644
--- a/scouting/webserver/requests/messages/request_all_matches_response.fbs
+++ b/scouting/webserver/requests/messages/request_all_matches_response.fbs
@@ -1,5 +1,15 @@
namespace scouting.webserver.requests;
+// Specifies whether a team has been scouted for this particular match.
+table ScoutedLevel {
+ r1: bool (id: 0);
+ r2: bool (id: 1);
+ r3: bool (id: 2);
+ b1: bool (id: 3);
+ b2: bool (id: 4);
+ b3: bool (id: 5);
+}
+
table Match {
match_number:int (id: 0);
set_number:int (id: 1);
@@ -10,6 +20,9 @@
b1:int (id: 6);
b2:int (id: 7);
b3:int (id: 8);
+
+ // Tells you how completely we've data scouted this match.
+ data_scouted: ScoutedLevel (id: 9);
}
table RequestAllMatchesResponse {
diff --git a/scouting/webserver/requests/requests.go b/scouting/webserver/requests/requests.go
index 062398a..39f344e 100644
--- a/scouting/webserver/requests/requests.go
+++ b/scouting/webserver/requests/requests.go
@@ -79,6 +79,7 @@
ReturnAllShifts() ([]db.Shift, error)
ReturnStats() ([]db.Stats, error)
ReturnStats2023() ([]db.Stats2023, error)
+ ReturnStats2023ForTeam(teamNumber string, matchNumber int32, setNumber int32, compLevel string) ([]db.Stats2023, error)
QueryAllShifts(int) ([]db.Shift, error)
QueryStats(int) ([]db.Stats, error)
QueryNotes(int32) ([]string, error)
@@ -212,6 +213,16 @@
db Database
}
+// Change structure of match objects in the database(1 per team) to
+// the old match structure(1 per match) that the webserver uses.
+// We use the information in this struct to identify which match object
+// corresponds to which old match structure object.
+type MatchAssemblyKey struct {
+ MatchNumber int32
+ SetNumber int32
+ CompLevel string
+}
+
func findIndexInList(list []string, comp_level string) (int, error) {
for index, value := range list {
if value == comp_level {
@@ -221,6 +232,15 @@
return -1, errors.New(fmt.Sprint("Failed to find comp level ", comp_level, " in list ", list))
}
+func (handler requestAllMatchesHandler) teamHasBeenDataScouted(key MatchAssemblyKey, teamNumber int32) (bool, error) {
+ stats, err := handler.db.ReturnStats2023ForTeam(
+ strconv.Itoa(int(teamNumber)), key.MatchNumber, key.SetNumber, key.CompLevel)
+ if err != nil {
+ return false, err
+ }
+ return (len(stats) > 0), nil
+}
+
func (handler requestAllMatchesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
requestBytes, err := io.ReadAll(req.Body)
if err != nil {
@@ -239,35 +259,42 @@
return
}
- // Change structure of match objects in the database(1 per team) to
- // the old match structure(1 per match) that the webserver uses.
- type Key struct {
- MatchNumber int32
- SetNumber int32
- CompLevel string
- }
-
- assembledMatches := map[Key]request_all_matches_response.MatchT{}
+ assembledMatches := map[MatchAssemblyKey]request_all_matches_response.MatchT{}
for _, match := range matches {
- key := Key{match.MatchNumber, match.SetNumber, match.CompLevel}
+ key := MatchAssemblyKey{match.MatchNumber, match.SetNumber, match.CompLevel}
+
+ // Retrieve the converted match structure we have assembled so
+ // far. If we haven't started assembling one yet, then start a
+ // new one.
entry, ok := assembledMatches[key]
if !ok {
entry = request_all_matches_response.MatchT{
MatchNumber: match.MatchNumber,
SetNumber: match.SetNumber,
CompLevel: match.CompLevel,
+ DataScouted: &request_all_matches_response.ScoutedLevelT{},
}
}
+
+ var team *int32
+ var dataScoutedTeam *bool
+
+ // Fill in the field for the match that we have in in the
+ // database. In the database, each match row only has 1 team
+ // number.
switch match.Alliance {
case "R":
switch match.AlliancePosition {
case 1:
- entry.R1 = match.TeamNumber
+ team = &entry.R1
+ dataScoutedTeam = &entry.DataScouted.R1
case 2:
- entry.R2 = match.TeamNumber
+ team = &entry.R2
+ dataScoutedTeam = &entry.DataScouted.R2
case 3:
- entry.R3 = match.TeamNumber
+ team = &entry.R3
+ dataScoutedTeam = &entry.DataScouted.R3
default:
respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown red position ", strconv.Itoa(int(match.AlliancePosition)), " in match ", strconv.Itoa(int(match.MatchNumber))))
return
@@ -275,11 +302,14 @@
case "B":
switch match.AlliancePosition {
case 1:
- entry.B1 = match.TeamNumber
+ team = &entry.B1
+ dataScoutedTeam = &entry.DataScouted.B1
case 2:
- entry.B2 = match.TeamNumber
+ team = &entry.B2
+ dataScoutedTeam = &entry.DataScouted.B2
case 3:
- entry.B3 = match.TeamNumber
+ team = &entry.B3
+ dataScoutedTeam = &entry.DataScouted.B3
default:
respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown blue position ", strconv.Itoa(int(match.AlliancePosition)), " in match ", strconv.Itoa(int(match.MatchNumber))))
return
@@ -288,6 +318,21 @@
respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Unknown alliance ", match.Alliance, " in match ", strconv.Itoa(int(match.AlliancePosition))))
return
}
+
+ *team = match.TeamNumber
+
+ // Figure out if this team has been data scouted already.
+ *dataScoutedTeam, err = handler.teamHasBeenDataScouted(key, match.TeamNumber)
+ if err != nil {
+ respondWithError(w, http.StatusInternalServerError, fmt.Sprint(
+ "Failed to determine data scouting status for team ",
+ strconv.Itoa(int(match.AlliancePosition)),
+ " in match ",
+ strconv.Itoa(int(match.MatchNumber)),
+ err))
+ return
+ }
+
assembledMatches[key] = entry
}
diff --git a/scouting/webserver/requests/requests_test.go b/scouting/webserver/requests/requests_test.go
index 26b79c5..dbad955 100644
--- a/scouting/webserver/requests/requests_test.go
+++ b/scouting/webserver/requests/requests_test.go
@@ -199,6 +199,29 @@
Alliance: "B", AlliancePosition: 3, TeamNumber: 202,
},
},
+ // Pretend that we have some data scouting data.
+ stats2023: []db.Stats2023{
+ {
+ TeamNumber: "5", MatchNumber: 1, SetNumber: 1,
+ CompLevel: "qm", 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,
+ AvgCycle: 34, CollectedBy: "alex",
+ },
+ {
+ TeamNumber: "973", MatchNumber: 3, SetNumber: 1,
+ CompLevel: "qm", 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,
+ AvgCycle: 53, CollectedBy: "bob",
+ },
+ },
}
scoutingServer := server.NewScoutingServer()
HandleRequests(&db, scoutingServer)
@@ -220,14 +243,27 @@
{
1, 1, "qm",
5, 42, 600, 971, 400, 200,
+ &request_all_matches_response.ScoutedLevelT{
+ // The R1 team has already been data
+ // scouted.
+ true, false, false, false, false, false,
+ },
},
{
2, 1, "qm",
6, 43, 601, 972, 401, 201,
+ &request_all_matches_response.ScoutedLevelT{
+ false, false, false, false, false, false,
+ },
},
{
3, 1, "qm",
7, 44, 602, 973, 402, 202,
+ &request_all_matches_response.ScoutedLevelT{
+ // The B1 team has already been data
+ // scouted.
+ false, false, false, true, false, false,
+ },
},
},
}
@@ -874,6 +910,16 @@
return database.stats2023, nil
}
+func (database *MockDatabase) ReturnStats2023ForTeam(teamNumber string, matchNumber int32, setNumber int32, compLevel string) ([]db.Stats2023, error) {
+ var results []db.Stats2023
+ for _, stats := range database.stats2023 {
+ if stats.TeamNumber == teamNumber && stats.MatchNumber == matchNumber && stats.SetNumber == setNumber && stats.CompLevel == compLevel {
+ results = append(results, stats)
+ }
+ }
+ return results, nil
+}
+
func (database *MockDatabase) QueryStats(int) ([]db.Stats, error) {
return []db.Stats{}, nil
}