Add climbing level to database

I spent a lot of time trying to make this use enums for the entire
data path. Unfortunately, I ran into a few issues. Firstly, I couldn't
figure out how make our Go SQL code happy with postgresql enums. I
kept getting errors about `unknown oid`. Secondly, I couldn't figure
out how to de-duplicate the enum between `submit_data_scouting.fbs`
and `request_data_scouting_response.fbs`. The generated Go code
doesn't import the dependency properly.

All this turned into an enum at the flatbuffer and TypeScript level,
but just an integer at the Go/postgres level.

A future patch can deal with this. Perhaps it'd be better to ignore
this altogether and just switch to a library like Gorm.

Signed-off-by: Philipp Schrader <philipp.schrader@gmail.com>
Change-Id: Id6cbb5502fd77f3107514b8d7cb9df2923a9d5f9
diff --git a/scouting/webserver/requests/debug/cli/cli_test.py b/scouting/webserver/requests/debug/cli/cli_test.py
index 8bebc69..718b2ce 100644
--- a/scouting/webserver/requests/debug/cli/cli_test.py
+++ b/scouting/webserver/requests/debug/cli/cli_test.py
@@ -75,7 +75,7 @@
             "upper_goal_tele": 14,
             "lower_goal_tele": 15,
             "defense_rating": 3,
-            "climbing": 1,
+            "climb_level": "Medium",
         })
         exit_code, _, stderr = run_debug_cli(["-submitDataScouting", json_path])
         self.assertEqual(exit_code, 0, stderr)
@@ -97,14 +97,14 @@
             UpperGoalTele: (int32) 14,
             LowerGoalTele: (int32) 15,
             DefenseRating: (int32) 3,
-            Climbing: (int32) 1,
             CollectedBy: (string) (len=9) "debug_cli",
             AutoBall1: (bool) true,
             AutoBall2: (bool) false,
             AutoBall3: (bool) false,
             AutoBall4: (bool) false,
             AutoBall5: (bool) true,
-            StartingQuadrant: (int32) 3
+            StartingQuadrant: (int32) 3,
+            ClimbLevel: (request_data_scouting_response.ClimbLevel) Medium
             }"""), stdout)
 
     def test_request_all_matches(self):
diff --git a/scouting/webserver/requests/messages/request_data_scouting_response.fbs b/scouting/webserver/requests/messages/request_data_scouting_response.fbs
index 7d91cb7..d85dfbb 100644
--- a/scouting/webserver/requests/messages/request_data_scouting_response.fbs
+++ b/scouting/webserver/requests/messages/request_data_scouting_response.fbs
@@ -1,5 +1,18 @@
 namespace scouting.webserver.requests;
 
+// TODO(phil): Deduplicate with submit_data_scouting.
+// At the moment, our Go setup doesn't handle includes.
+enum ClimbLevel : byte {
+    NoAttempt = 0,
+    Failed,
+    // Tried for more than 10 seconds and failed.
+    FailedWithPlentyOfTime,
+    Low,
+    Medium,
+    High,
+    Transversal,
+}
+
 table Stats {
     team:int (id: 0);
     match:int (id: 1);
@@ -11,7 +24,10 @@
     upper_goal_tele:int (id:6);
     lower_goal_tele:int (id:7);
     defense_rating:int (id:8);
-    climbing:int (id:9);
+
+    climbing:int (id:9, deprecated);
+    climb_level:ClimbLevel (id:17);
+
     collected_by:string (id:10);
 
     auto_ball_1:bool (id:11);
diff --git a/scouting/webserver/requests/messages/submit_data_scouting.fbs b/scouting/webserver/requests/messages/submit_data_scouting.fbs
index d3d87e2..a9c44a2 100644
--- a/scouting/webserver/requests/messages/submit_data_scouting.fbs
+++ b/scouting/webserver/requests/messages/submit_data_scouting.fbs
@@ -1,5 +1,18 @@
 namespace scouting.webserver.requests;
 
+// TODO(phil): Deduplicate with request_scouting_data_response.
+// At the moment, our Go setup doesn't handle includes.
+enum ClimbLevel : byte {
+    NoAttempt = 0,
+    Failed,
+    // Tried for more than 10 seconds and failed.
+    FailedWithPlentyOfTime,
+    Low,
+    Medium,
+    High,
+    Transversal,
+}
+
 table SubmitDataScouting {
     team:int (id: 0);
     match:int (id: 1);
@@ -18,9 +31,10 @@
     // TODO: Document what the different values mean. E.g. 0 means no defense
     // played against this robot?
     defense_received_rating:int (id:10);
-    // The rating that this robot gets for its climbing.
-    // TODO: Change into an enum to make the different values self-documenting.
-    climbing:int (id:9);
+
+    climbing:int (id:9, deprecated);
+    climb_level:ClimbLevel (id:17);
+
     auto_ball_1:bool (id:11);
     auto_ball_2:bool (id:12);
     auto_ball_3:bool (id:13);
diff --git a/scouting/webserver/requests/requests.go b/scouting/webserver/requests/requests.go
index 13d7396..118fafb 100644
--- a/scouting/webserver/requests/requests.go
+++ b/scouting/webserver/requests/requests.go
@@ -156,7 +156,7 @@
 		UpperGoalShots:  request.UpperGoalTele(),
 		LowerGoalShots:  request.LowerGoalTele(),
 		PlayedDefense:   request.DefenseRating(),
-		Climbing:        request.Climbing(),
+		Climbing:        int32(request.ClimbLevel()),
 		CollectedBy:     username,
 	}
 
@@ -344,7 +344,7 @@
 			UpperGoalTele:    stat.UpperGoalShots,
 			LowerGoalTele:    stat.LowerGoalShots,
 			DefenseRating:    stat.PlayedDefense,
-			Climbing:         stat.Climbing,
+			ClimbLevel:       request_data_scouting_response.ClimbLevel(stat.Climbing),
 			CollectedBy:      stat.CollectedBy,
 		})
 	}
diff --git a/scouting/webserver/requests/requests_test.go b/scouting/webserver/requests/requests_test.go
index 12d918b..8165c7d 100644
--- a/scouting/webserver/requests/requests_test.go
+++ b/scouting/webserver/requests/requests_test.go
@@ -97,7 +97,7 @@
 		UpperGoalTele:    9971,
 		LowerGoalTele:    9971,
 		DefenseRating:    9971,
-		Climbing:         9971,
+		ClimbLevel:       submit_data_scouting.ClimbLevelLow,
 	}).Pack(builder))
 
 	response, err := debug.SubmitDataScouting("http://localhost:8080", builder.FinishedBytes())
@@ -231,7 +231,7 @@
 				AutoBallPickedUp: [5]bool{true, false, false, false, true},
 				ShotsMissed:      1, UpperGoalShots: 2, LowerGoalShots: 3,
 				ShotsMissedAuto: 4, UpperGoalAuto: 5, LowerGoalAuto: 6,
-				PlayedDefense: 7, Climbing: 8,
+				PlayedDefense: 7, Climbing: 2,
 				CollectedBy: "john",
 			},
 			{
@@ -240,7 +240,7 @@
 				AutoBallPickedUp: [5]bool{false, false, true, false, false},
 				ShotsMissed:      2, UpperGoalShots: 3, LowerGoalShots: 4,
 				ShotsMissedAuto: 5, UpperGoalAuto: 6, LowerGoalAuto: 7,
-				PlayedDefense: 8, Climbing: 9,
+				PlayedDefense: 8, Climbing: 4,
 				CollectedBy: "andrea",
 			},
 		},
@@ -260,33 +260,27 @@
 
 	expected := request_data_scouting_response.RequestDataScoutingResponseT{
 		StatsList: []*request_data_scouting_response.StatsT{
-			// Team, Match,
-			// MissedShotsAuto, UpperGoalAuto, LowerGoalAuto,
-			// MissedShotsTele, UpperGoalTele, LowerGoalTele,
-			// DefenseRating, Climbing,
-			// CollectedBy,
-			// AutoBall1, AutoBall2, AutoBall3,
-			// AutoBall4, AutoBall5,
-			// StartingQuadrant,
 			{
-				971, 1,
-				4, 5, 6,
-				1, 2, 3,
-				7, 8,
-				"john",
-				true, false, false,
-				false, true,
-				1,
+				Team: 971, Match: 1,
+				MissedShotsAuto: 4, UpperGoalAuto: 5, LowerGoalAuto: 6,
+				MissedShotsTele: 1, UpperGoalTele: 2, LowerGoalTele: 3,
+				DefenseRating: 7,
+				CollectedBy:   "john",
+				AutoBall1:     true, AutoBall2: false, AutoBall3: false,
+				AutoBall4: false, AutoBall5: true,
+				StartingQuadrant: 1,
+				ClimbLevel:       request_data_scouting_response.ClimbLevelFailedWithPlentyOfTime,
 			},
 			{
-				972, 1,
-				5, 6, 7,
-				2, 3, 4,
-				8, 9,
-				"andrea",
-				false, false, true,
-				false, false,
-				2,
+				Team: 972, Match: 1,
+				MissedShotsAuto: 5, UpperGoalAuto: 6, LowerGoalAuto: 7,
+				MissedShotsTele: 2, UpperGoalTele: 3, LowerGoalTele: 4,
+				DefenseRating: 8,
+				CollectedBy:   "andrea",
+				AutoBall1:     false, AutoBall2: false, AutoBall3: true,
+				AutoBall4: false, AutoBall5: false,
+				StartingQuadrant: 2,
+				ClimbLevel:       request_data_scouting_response.ClimbLevelMedium,
 			},
 		},
 	}