Merge changes I242abbf2,Ib3e68103,I8c324c1d,Id8967468

* changes:
  scouting: Add explanations on the app for docked/engaged
  scouting: Convert actions to stats when submitted
  Make the scouting webserver accept prescouting data
  Add pre-scouting support in the scouting database
diff --git a/scouting/db/db.go b/scouting/db/db.go
index 031f54c..1282638 100644
--- a/scouting/db/db.go
+++ b/scouting/db/db.go
@@ -28,6 +28,11 @@
 }
 
 type Stats2023 struct {
+	// This is set to `true` for "pre-scouted" matches. This means that the
+	// match information is unlikely to correspond with an entry in the
+	// `TeamMatch` table.
+	PreScouting bool `gorm:"primaryKey"`
+
 	TeamNumber                                                     string `gorm:"primaryKey"`
 	MatchNumber                                                    int32  `gorm:"primaryKey"`
 	SetNumber                                                      int32  `gorm:"primaryKey"`
@@ -50,6 +55,7 @@
 }
 
 type Action struct {
+	PreScouting bool   `gorm:"primaryKey"`
 	TeamNumber  string `gorm:"primaryKey"`
 	MatchNumber int32  `gorm:"primaryKey"`
 	SetNumber   int32  `gorm:"primaryKey"`
@@ -155,28 +161,30 @@
 }
 
 func (database *Database) AddAction(a Action) error {
-	result := database.Clauses(clause.OnConflict{
-		UpdateAll: true,
-	}).Create(&a)
+	// TODO(phil): Add check for a corresponding match in the `TeamMatch`
+	// table. Similar to `AddToStats2023()` below.
+	result := database.Create(&a)
 	return result.Error
 }
 
 func (database *Database) AddToStats2023(s Stats2023) error {
-	matches, err := database.QueryMatchesString(s.TeamNumber)
-	if err != nil {
-		return err
-	}
-	foundMatch := false
-	for _, match := range matches {
-		if match.MatchNumber == s.MatchNumber {
-			foundMatch = true
-			break
+	if !s.PreScouting {
+		matches, err := database.QueryMatchesString(s.TeamNumber)
+		if err != nil {
+			return err
 		}
-	}
-	if !foundMatch {
-		return errors.New(fmt.Sprint(
-			"Failed to find team ", s.TeamNumber,
-			" in match ", s.MatchNumber, " in the schedule."))
+		foundMatch := false
+		for _, match := range matches {
+			if match.MatchNumber == s.MatchNumber {
+				foundMatch = true
+				break
+			}
+		}
+		if !foundMatch {
+			return errors.New(fmt.Sprint(
+				"Failed to find team ", s.TeamNumber,
+				" in match ", s.MatchNumber, " in the schedule."))
+		}
 	}
 
 	result := database.Create(&s)
diff --git a/scouting/db/db_test.go b/scouting/db/db_test.go
index 3c345b0..663a080 100644
--- a/scouting/db/db_test.go
+++ b/scouting/db/db_test.go
@@ -144,7 +144,8 @@
 
 	correct := []Stats2023{
 		Stats2023{
-			TeamNumber: "6344", MatchNumber: 3, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "6344", MatchNumber: 3, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 1, LowCubesAuto: 0,
 			MiddleCubesAuto: 1, HighCubesAuto: 0, CubesDroppedAuto: 1,
 			LowConesAuto: 1, MiddleConesAuto: 0, HighConesAuto: 2,
@@ -156,7 +157,8 @@
 			BalanceAttempt: false, CollectedBy: "emma",
 		},
 		Stats2023{
-			TeamNumber: "7454", MatchNumber: 3, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "7454", MatchNumber: 3, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 2, LowCubesAuto: 1,
 			MiddleCubesAuto: 2, HighCubesAuto: 2, CubesDroppedAuto: 0,
 			LowConesAuto: 2, MiddleConesAuto: 0, HighConesAuto: 0,
@@ -168,7 +170,8 @@
 			BalanceAttempt: false, CollectedBy: "tyler",
 		},
 		Stats2023{
-			TeamNumber: "4354", MatchNumber: 3, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "4354", MatchNumber: 3, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 3, LowCubesAuto: 0,
 			MiddleCubesAuto: 1, HighCubesAuto: 1, CubesDroppedAuto: 0,
 			LowConesAuto: 0, MiddleConesAuto: 2, HighConesAuto: 1,
@@ -180,7 +183,8 @@
 			BalanceAttempt: true, CollectedBy: "isaac",
 		},
 		Stats2023{
-			TeamNumber: "6533", MatchNumber: 3, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "6533", MatchNumber: 3, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 1, LowCubesAuto: 0,
 			MiddleCubesAuto: 2, HighCubesAuto: 1, CubesDroppedAuto: 1,
 			LowConesAuto: 1, MiddleConesAuto: 0, HighConesAuto: 0,
@@ -192,7 +196,8 @@
 			BalanceAttempt: true, CollectedBy: "will",
 		},
 		Stats2023{
-			TeamNumber: "8354", MatchNumber: 3, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "8354", MatchNumber: 3, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 2, LowCubesAuto: 1,
 			MiddleCubesAuto: 1, HighCubesAuto: 2, CubesDroppedAuto: 0,
 			LowConesAuto: 0, MiddleConesAuto: 1, HighConesAuto: 1,
@@ -236,13 +241,47 @@
 	}
 }
 
+func TestInsertPreScoutedStats(t *testing.T) {
+	fixture := createDatabase(t)
+	defer fixture.TearDown()
+
+	stats := Stats2023{
+		PreScouting: false,
+		TeamNumber:  "6344", MatchNumber: 3, SetNumber: 1,
+		CompLevel: "quals", StartingQuadrant: 1, LowCubesAuto: 0,
+		MiddleCubesAuto: 1, HighCubesAuto: 0, CubesDroppedAuto: 1,
+		LowConesAuto: 1, MiddleConesAuto: 0, HighConesAuto: 2,
+		ConesDroppedAuto: 0, LowCubes: 1, MiddleCubes: 2,
+		HighCubes: 1, CubesDropped: 0, LowCones: 0,
+		MiddleCones: 2, HighCones: 1, ConesDropped: 1, SuperchargedPieces: 0,
+		AvgCycle: 0, Mobility: true, DockedAuto: true, EngagedAuto: false,
+		BalanceAttemptAuto: false, Docked: false, Engaged: false,
+		BalanceAttempt: false, CollectedBy: "emma",
+	}
+
+	// Attempt to insert the non-pre-scouted data and make sure it fails.
+	err := fixture.db.AddToStats2023(stats)
+	if err == nil {
+		t.Fatal("Expected error from inserting the stats.")
+	}
+	if err.Error() != "Failed to find team 6344 in match 3 in the schedule." {
+		t.Fatal("Got:", err.Error())
+	}
+
+	// Mark the data as pre-scouting data. It should now succeed.
+	stats.PreScouting = true
+	err = fixture.db.AddToStats2023(stats)
+	check(t, err, "Failed to add prescouted stats to DB")
+}
+
 func TestQueryingStats2023ByTeam(t *testing.T) {
 	fixture := createDatabase(t)
 	defer fixture.TearDown()
 
 	stats := []Stats2023{
 		Stats2023{
-			TeamNumber: "6344", MatchNumber: 3, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "6344", MatchNumber: 3, SetNumber: 1,
 			CompLevel: "qm", StartingQuadrant: 1, LowCubesAuto: 0,
 			MiddleCubesAuto: 1, HighCubesAuto: 0, CubesDroppedAuto: 1,
 			LowConesAuto: 1, MiddleConesAuto: 0, HighConesAuto: 2,
@@ -254,7 +293,8 @@
 			BalanceAttempt: false, CollectedBy: "emma",
 		},
 		Stats2023{
-			TeamNumber: "7454", MatchNumber: 4, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "7454", MatchNumber: 4, SetNumber: 1,
 			CompLevel: "qm", StartingQuadrant: 2, LowCubesAuto: 1,
 			MiddleCubesAuto: 2, HighCubesAuto: 2, CubesDroppedAuto: 0,
 			LowConesAuto: 2, MiddleConesAuto: 0, HighConesAuto: 0,
@@ -266,7 +306,8 @@
 			BalanceAttempt: false, CollectedBy: "tyler",
 		},
 		Stats2023{
-			TeamNumber: "6344", MatchNumber: 5, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "6344", MatchNumber: 5, SetNumber: 1,
 			CompLevel: "qm", StartingQuadrant: 1, LowCubesAuto: 0,
 			MiddleCubesAuto: 1, HighCubesAuto: 0, CubesDroppedAuto: 1,
 			LowConesAuto: 1, MiddleConesAuto: 0, HighConesAuto: 2,
@@ -323,7 +364,8 @@
 
 	startingStats := []Stats2023{
 		Stats2023{
-			TeamNumber: "1111", MatchNumber: 5, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "1111", MatchNumber: 5, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 2, LowCubesAuto: 2,
 			MiddleCubesAuto: 0, HighCubesAuto: 1, CubesDroppedAuto: 1,
 			LowConesAuto: 0, MiddleConesAuto: 2, HighConesAuto: 0,
@@ -335,7 +377,8 @@
 			BalanceAttempt: false, CollectedBy: "unknown",
 		},
 		Stats2023{
-			TeamNumber: "2314", MatchNumber: 5, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "2314", MatchNumber: 5, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 3, LowCubesAuto: 1,
 			MiddleCubesAuto: 0, HighCubesAuto: 1, CubesDroppedAuto: 1,
 			LowConesAuto: 0, MiddleConesAuto: 1, HighConesAuto: 0,
@@ -347,7 +390,8 @@
 			BalanceAttempt: false, CollectedBy: "simon",
 		},
 		Stats2023{
-			TeamNumber: "3242", MatchNumber: 5, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "3242", MatchNumber: 5, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 1, LowCubesAuto: 0,
 			MiddleCubesAuto: 2, HighCubesAuto: 0, CubesDroppedAuto: 1,
 			LowConesAuto: 1, MiddleConesAuto: 0, HighConesAuto: 0,
@@ -359,7 +403,8 @@
 			BalanceAttempt: false, CollectedBy: "eliza",
 		},
 		Stats2023{
-			TeamNumber: "1742", MatchNumber: 5, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "1742", MatchNumber: 5, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 4, LowCubesAuto: 1,
 			MiddleCubesAuto: 1, HighCubesAuto: 0, CubesDroppedAuto: 0,
 			LowConesAuto: 0, MiddleConesAuto: 2, HighConesAuto: 0,
@@ -370,7 +415,8 @@
 			Docked: false, Engaged: false, CollectedBy: "isaac",
 		},
 		Stats2023{
-			TeamNumber: "2454", MatchNumber: 5, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "2454", MatchNumber: 5, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 1, LowCubesAuto: 0,
 			MiddleCubesAuto: 0, HighCubesAuto: 0, CubesDroppedAuto: 0,
 			LowConesAuto: 1, MiddleConesAuto: 1, HighConesAuto: 0,
@@ -385,7 +431,8 @@
 
 	correct := []Stats2023{
 		Stats2023{
-			TeamNumber: "3242", MatchNumber: 5, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "3242", MatchNumber: 5, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 1, LowCubesAuto: 0,
 			MiddleCubesAuto: 2, HighCubesAuto: 0, CubesDroppedAuto: 1,
 			LowConesAuto: 1, MiddleConesAuto: 0, HighConesAuto: 0,
@@ -397,7 +444,8 @@
 			BalanceAttempt: false, CollectedBy: "eliza",
 		},
 		Stats2023{
-			TeamNumber: "2454", MatchNumber: 5, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "2454", MatchNumber: 5, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 1, LowCubesAuto: 0,
 			MiddleCubesAuto: 0, HighCubesAuto: 0, CubesDroppedAuto: 0,
 			LowConesAuto: 1, MiddleConesAuto: 1, HighConesAuto: 0,
@@ -677,7 +725,8 @@
 
 	correct := []Stats2023{
 		Stats2023{
-			TeamNumber: "2343", MatchNumber: 2, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "2343", MatchNumber: 2, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 1, LowCubesAuto: 1,
 			MiddleCubesAuto: 2, HighCubesAuto: 2, CubesDroppedAuto: 1,
 			LowConesAuto: 0, MiddleConesAuto: 0, HighConesAuto: 2,
@@ -689,7 +738,8 @@
 			BalanceAttempt: true, CollectedBy: "isaac",
 		},
 		Stats2023{
-			TeamNumber: "5443", MatchNumber: 2, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "5443", MatchNumber: 2, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 2, LowCubesAuto: 1,
 			MiddleCubesAuto: 1, HighCubesAuto: 0, CubesDroppedAuto: 1,
 			LowConesAuto: 1, MiddleConesAuto: 1, HighConesAuto: 0,
@@ -701,7 +751,8 @@
 			BalanceAttempt: false, CollectedBy: "jack",
 		},
 		Stats2023{
-			TeamNumber: "5436", MatchNumber: 2, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "5436", MatchNumber: 2, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 3, LowCubesAuto: 0,
 			MiddleCubesAuto: 2, HighCubesAuto: 0, CubesDroppedAuto: 1,
 			LowConesAuto: 2, MiddleConesAuto: 0, HighConesAuto: 0,
@@ -713,7 +764,8 @@
 			BalanceAttempt: true, CollectedBy: "martin",
 		},
 		Stats2023{
-			TeamNumber: "5643", MatchNumber: 2, SetNumber: 1,
+			PreScouting: false,
+			TeamNumber:  "5643", MatchNumber: 2, SetNumber: 1,
 			CompLevel: "quals", StartingQuadrant: 4, LowCubesAuto: 0,
 			MiddleCubesAuto: 0, HighCubesAuto: 1, CubesDroppedAuto: 1,
 			LowConesAuto: 2, MiddleConesAuto: 0, HighConesAuto: 0,
diff --git a/scouting/scouting_test.cy.js b/scouting/scouting_test.cy.js
index 1d7620a..365b19e 100644
--- a/scouting/scouting_test.cy.js
+++ b/scouting/scouting_test.cy.js
@@ -1,5 +1,13 @@
 /// <reference types="cypress" />
 
+// On the 87th row of matches (index 86) click on the second team
+// (index 1) which resolves to team 5254 in semi final 2 match 3.
+const SEMI_FINAL_2_MATCH_3_TEAM_5254 = 86 * 6 + 1;
+
+// On the 1st row of matches (index 0) click on the fourth team
+// (index 3) which resolves to team 3990 in quals match 1.
+const QUALS_MATCH_1_TEAM_3990 = 0 * 6 + 3;
+
 function disableAlerts() {
   cy.get('#block_alerts').check({force: true}).should('be.checked');
 }
@@ -20,17 +28,6 @@
   cy.get(fieldSelector).type('{selectAll}' + value);
 }
 
-// Click on a random team in the Match list. The exact details here are not
-// important, but we need to know what they are. This could as well be any
-// other team from any other match.
-function clickSemiFinal2Match3Team5254() {
-  // On the 87th row of matches (index 86) click on the second team
-  // (index 1) which resolves to team 5254 in semi final 2 match 3.
-  cy.get('button.match-item')
-    .eq(86 * 6 + 1)
-    .click();
-}
-
 // Moves the nth slider left or right. A positive "adjustBy" value moves the
 // slider to the right. A negative value moves the slider to the left.
 //
@@ -115,7 +112,10 @@
 
   //TODO(FILIP): Verify last action when the last action header gets added.
   it('should: be able to submit data scouting.', () => {
-    clickSemiFinal2Match3Team5254();
+    // Click on a random team in the Match list. The exact details here are not
+    // important, but we need to know what they are. This could as well be any
+    // other team from any other match.
+    cy.get('button.match-item').eq(SEMI_FINAL_2_MATCH_3_TEAM_5254).click();
 
     // Select Starting Position.
     headerShouldBe('5254 Init ');
@@ -135,7 +135,7 @@
     clickButton('DEAD');
     clickButton('Revive');
 
-    // Engame.
+    // Endgame.
     clickButton('Endgame');
     cy.get('[type="checkbox"]').check();
 
@@ -144,10 +144,16 @@
 
     clickButton('Submit');
     headerShouldBe('5254 Success ');
+
+    // Now that the data is submitted, the button should be disabled.
+    switchToTab('Match List');
+    cy.get('button.match-item')
+      .eq(SEMI_FINAL_2_MATCH_3_TEAM_5254)
+      .should('be.disabled');
   });
 
   it('should: be able to return to correct screen with undo for pick and place.', () => {
-    clickSemiFinal2Match3Team5254();
+    cy.get('button.match-item').eq(QUALS_MATCH_1_TEAM_3990).click();
 
     // Select Starting Position.
     cy.get('[type="radio"]').first().check();
@@ -160,13 +166,13 @@
     clickButton('UNDO');
 
     // User should be back on pickup screen.
-    headerShouldBe('5254 Pickup ');
+    headerShouldBe('3990 Pickup ');
 
     // Check the same thing but for undoing place.
     clickButton('CUBE');
     clickButton('MID');
     clickButton('UNDO');
-    headerShouldBe('5254 Place ');
+    headerShouldBe('3990 Place ');
   });
 
   it('should: submit note scouting for multiple teams', () => {
diff --git a/scouting/webserver/requests/messages/submit_actions.fbs b/scouting/webserver/requests/messages/submit_actions.fbs
index a0f7913..869c2f0 100644
--- a/scouting/webserver/requests/messages/submit_actions.fbs
+++ b/scouting/webserver/requests/messages/submit_actions.fbs
@@ -68,6 +68,13 @@
     set_number:int (id: 2);
     comp_level:string (id: 3);
     actions_list:[Action] (id:4);
-    //TODO: delete this field
-    collected_by:string (id: 5);
+
+    // Do not use this field. The information is collected by the webserver,
+    // not the web page.
+    collected_by:string (id: 5, deprecated);
+
+    // If this is for pre-scouting, then the server should accept this
+    // submission. I.e. checking that the match information exists in the match
+    // list should be skipped.
+    pre_scouting:bool (id: 6);
 }
diff --git a/scouting/webserver/requests/requests.go b/scouting/webserver/requests/requests.go
index 25bb6b7..8293fa2 100644
--- a/scouting/webserver/requests/requests.go
+++ b/scouting/webserver/requests/requests.go
@@ -377,10 +377,12 @@
 	cycles := int64(0)
 	picked_up := false
 	lastPlacedTime := int64(0)
-	stat := db.Stats2023{TeamNumber: string(submitActions.TeamNumber()), MatchNumber: submitActions.MatchNumber(), SetNumber: submitActions.SetNumber(), CompLevel: string(submitActions.CompLevel()),
+	stat := db.Stats2023{
+		PreScouting: submitActions.PreScouting(),
+		TeamNumber:  string(submitActions.TeamNumber()), MatchNumber: submitActions.MatchNumber(), SetNumber: submitActions.SetNumber(), CompLevel: string(submitActions.CompLevel()),
 		StartingQuadrant: 0, LowCubesAuto: 0, MiddleCubesAuto: 0, HighCubesAuto: 0, CubesDroppedAuto: 0,
 		LowConesAuto: 0, MiddleConesAuto: 0, HighConesAuto: 0, ConesDroppedAuto: 0, LowCubes: 0, MiddleCubes: 0, HighCubes: 0,
-		CubesDropped: 0, LowCones: 0, MiddleCones: 0, HighCones: 0, ConesDropped: 0, SuperchargedPieces: 0, AvgCycle: 0, CollectedBy: string(submitActions.CollectedBy()),
+		CubesDropped: 0, LowCones: 0, MiddleCones: 0, HighCones: 0, ConesDropped: 0, SuperchargedPieces: 0, AvgCycle: 0, CollectedBy: "",
 	}
 	// Loop over all actions.
 	for i := 0; i < submitActions.ActionsListLength(); i++ {
@@ -825,6 +827,7 @@
 		request.ActionsList(&action, i)
 
 		dbAction := db.Action{
+			PreScouting: request.PreScouting(),
 			TeamNumber:  string(request.TeamNumber()),
 			MatchNumber: request.MatchNumber(),
 			SetNumber:   request.SetNumber(),
@@ -849,6 +852,20 @@
 		}
 	}
 
+	stats, err := ConvertActionsToStat(request)
+	if err != nil {
+		respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to convert actions to stats: ", err))
+		return
+	}
+
+	stats.CollectedBy = username
+
+	err = handler.db.AddToStats2023(stats)
+	if err != nil {
+		respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to submit stats: ", stats, ": ", err))
+		return
+	}
+
 	builder := flatbuffers.NewBuilder(50 * 1024)
 	builder.Finish((&SubmitActionsResponseT{}).Pack(builder))
 	w.Write(builder.FinishedBytes())
diff --git a/scouting/webserver/requests/requests_test.go b/scouting/webserver/requests/requests_test.go
index c2bc750..d68dd61 100644
--- a/scouting/webserver/requests/requests_test.go
+++ b/scouting/webserver/requests/requests_test.go
@@ -292,7 +292,6 @@
 		MatchNumber: 3,
 		SetNumber:   1,
 		CompLevel:   "quals",
-		CollectedBy: "katie",
 		ActionsList: []*submit_actions.ActionT{
 			{
 				ActionTaken: &submit_actions.ActionTypeT{
@@ -408,6 +407,7 @@
 				Timestamp: 4200,
 			},
 		},
+		PreScouting: false,
 	}).Pack(builder))
 
 	submitActions := submit_actions.GetRootAsSubmitActions(builder.FinishedBytes(), 0)
@@ -418,7 +418,8 @@
 	}
 
 	expected := db.Stats2023{
-		TeamNumber: "4244", MatchNumber: 3, SetNumber: 1,
+		PreScouting: false,
+		TeamNumber:  "4244", MatchNumber: 3, SetNumber: 1,
 		CompLevel: "quals", StartingQuadrant: 1, LowCubesAuto: 1,
 		MiddleCubesAuto: 0, HighCubesAuto: 0, CubesDroppedAuto: 1,
 		LowConesAuto: 0, MiddleConesAuto: 0, HighConesAuto: 0,
@@ -427,7 +428,7 @@
 		MiddleCones: 0, HighCones: 1, ConesDropped: 0, SuperchargedPieces: 1,
 		AvgCycle: 950, Mobility: true, DockedAuto: true, EngagedAuto: true,
 		BalanceAttemptAuto: false, Docked: true, Engaged: false,
-		BalanceAttempt: true, CollectedBy: "katie",
+		BalanceAttempt: true, CollectedBy: "",
 	}
 
 	if expected != response {
@@ -824,6 +825,7 @@
 				Timestamp: 1009,
 			},
 		},
+		PreScouting: true,
 	}).Pack(builder))
 
 	_, err := debug.SubmitActions("http://localhost:8080", builder.FinishedBytes())
@@ -860,6 +862,7 @@
 
 	expectedActions := []db.Action{
 		{
+			PreScouting:     true,
 			TeamNumber:      "1234",
 			MatchNumber:     4,
 			SetNumber:       1,
@@ -869,6 +872,7 @@
 			Timestamp:       2400,
 		},
 		{
+			PreScouting:     true,
 			TeamNumber:      "1234",
 			MatchNumber:     4,
 			SetNumber:       1,
@@ -879,10 +883,28 @@
 		},
 	}
 
+	expectedStats := []db.Stats2023{
+		db.Stats2023{
+			PreScouting: true,
+			TeamNumber:  "1234", MatchNumber: 4, SetNumber: 1,
+			CompLevel: "qual", StartingQuadrant: 0, LowCubesAuto: 0,
+			MiddleCubesAuto: 0, HighCubesAuto: 0, CubesDroppedAuto: 0,
+			LowConesAuto: 0, MiddleConesAuto: 0, HighConesAuto: 0,
+			ConesDroppedAuto: 0, LowCubes: 1, MiddleCubes: 0,
+			HighCubes: 0, CubesDropped: 0, LowCones: 0,
+			MiddleCones: 0, HighCones: 0, ConesDropped: 0, SuperchargedPieces: 0,
+			AvgCycle: 0, Mobility: false, DockedAuto: false, EngagedAuto: false,
+			BalanceAttemptAuto: false, Docked: false, Engaged: false,
+			BalanceAttempt: false, CollectedBy: "debug_cli",
+		},
+	}
+
 	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
diff --git a/scouting/www/entry/entry.ng.html b/scouting/www/entry/entry.ng.html
index 98e427b..828e036 100644
--- a/scouting/www/entry/entry.ng.html
+++ b/scouting/www/entry/entry.ng.html
@@ -129,11 +129,11 @@
       <div *ngIf="autoPhase" class="d-grid gap-2">
         <label>
           <input #docked type="checkbox" />
-          Docked
+          Docked (on the charging station)
         </label>
         <label>
           <input #engaged type="checkbox" />
-          Engaged
+          Engaged (level &amp; station lights on)
         </label>
         <label>
           <input #attempted type="checkbox" />
@@ -219,11 +219,11 @@
       <div *ngIf="autoPhase" class="d-grid gap-1">
         <label>
           <input #docked type="checkbox" />
-          Docked
+          Docked (on the charging station)
         </label>
         <label>
           <input #engaged type="checkbox" />
-          Engaged
+          Engaged (level &amp; station lights on)
         </label>
         <label>
           <input #attempted type="checkbox" />
@@ -267,11 +267,11 @@
       </button>
       <label>
         <input #docked type="checkbox" />
-        Docked
+        Docked (on the charging station)
       </label>
       <label>
         <input #engaged type="checkbox" />
-        Engaged
+        Engaged (level &amp; station lights on)
       </label>
       <label>
         <input #attempted type="checkbox" />