Add alert for scouting

Alert scouters when someone else is scouting the same team.

Signed-off-by: Emily Markova <emily.markova@gmail.com>
Change-Id: Id62ad1a96ff90ef4cba1797c21bc4063ccbdeae5
diff --git a/scouting/webserver/main.go b/scouting/webserver/main.go
index d655708..6eca5a9 100644
--- a/scouting/webserver/main.go
+++ b/scouting/webserver/main.go
@@ -133,8 +133,9 @@
 	defer database.Delete()
 
 	scoutingServer := server.NewScoutingServer()
+	var realClock requests.RealClock
 	static.ServePages(scoutingServer, *dirPtr, database)
-	requests.HandleRequests(database, scoutingServer)
+	requests.HandleRequests(database, scoutingServer, realClock)
 	scoutingServer.Start(*portPtr)
 	fmt.Println("Serving", *dirPtr, "on port", *portPtr)
 
diff --git a/scouting/webserver/requests/BUILD b/scouting/webserver/requests/BUILD
index 07ebbe6..6c0460b 100644
--- a/scouting/webserver/requests/BUILD
+++ b/scouting/webserver/requests/BUILD
@@ -25,6 +25,8 @@
         "//scouting/webserver/requests/messages:request_all_notes_response_go_fbs",
         "//scouting/webserver/requests/messages:request_all_pit_images_go_fbs",
         "//scouting/webserver/requests/messages:request_all_pit_images_response_go_fbs",
+        "//scouting/webserver/requests/messages:request_current_scouting_go_fbs",
+        "//scouting/webserver/requests/messages:request_current_scouting_response_go_fbs",
         "//scouting/webserver/requests/messages:request_notes_for_team_go_fbs",
         "//scouting/webserver/requests/messages:request_notes_for_team_response_go_fbs",
         "//scouting/webserver/requests/messages:request_pit_images_go_fbs",
@@ -69,6 +71,8 @@
         "//scouting/webserver/requests/messages:request_all_notes_response_go_fbs",
         "//scouting/webserver/requests/messages:request_all_pit_images_go_fbs",
         "//scouting/webserver/requests/messages:request_all_pit_images_response_go_fbs",
+        "//scouting/webserver/requests/messages:request_current_scouting_go_fbs",
+        "//scouting/webserver/requests/messages:request_current_scouting_response_go_fbs",
         "//scouting/webserver/requests/messages:request_notes_for_team_go_fbs",
         "//scouting/webserver/requests/messages:request_pit_images_go_fbs",
         "//scouting/webserver/requests/messages:request_pit_images_response_go_fbs",
diff --git a/scouting/webserver/requests/debug/BUILD b/scouting/webserver/requests/debug/BUILD
index ff85206..7a78456 100644
--- a/scouting/webserver/requests/debug/BUILD
+++ b/scouting/webserver/requests/debug/BUILD
@@ -16,6 +16,7 @@
         "//scouting/webserver/requests/messages:request_all_matches_response_go_fbs",
         "//scouting/webserver/requests/messages:request_all_notes_response_go_fbs",
         "//scouting/webserver/requests/messages:request_all_pit_images_response_go_fbs",
+        "//scouting/webserver/requests/messages:request_current_scouting_response_go_fbs",
         "//scouting/webserver/requests/messages:request_notes_for_team_response_go_fbs",
         "//scouting/webserver/requests/messages:request_pit_images_response_go_fbs",
         "//scouting/webserver/requests/messages:request_shift_schedule_response_go_fbs",
diff --git a/scouting/webserver/requests/debug/cli/cli_test.py b/scouting/webserver/requests/debug/cli/cli_test.py
index cfdcd03..70a94c8 100644
--- a/scouting/webserver/requests/debug/cli/cli_test.py
+++ b/scouting/webserver/requests/debug/cli/cli_test.py
@@ -96,6 +96,15 @@
 
             time.sleep(0.25)
 
+    def test_request_current_scouting(self):
+        self.start_servers(year=2020, event_code="fake")
+
+        # First submit some data to be added to the database.
+        json_path = write_json_request({"team_number": "971"})
+        exit_code, _, stderr = run_debug_cli(
+            ["-requestCurrentScouting", json_path])
+        self.assertEqual(exit_code, 0, stderr)
+
     def test_submit_and_request_notes(self):
         self.start_servers(year=2020, event_code="fake")
 
diff --git a/scouting/webserver/requests/debug/cli/main.go b/scouting/webserver/requests/debug/cli/main.go
index a4346e5..f513c50 100644
--- a/scouting/webserver/requests/debug/cli/main.go
+++ b/scouting/webserver/requests/debug/cli/main.go
@@ -95,6 +95,8 @@
 		"If specified, parse the file as a requestAllDriverRankings JSON request.")
 	requestAllNotesPtr := flag.String("requestAllNotes", "",
 		"If specified, parse the file as a requestAllNotes JSON request.")
+	requestCurrentScoutingPtr := flag.String("requestCurrentScouting", "",
+		"If specified, parse the file as a requestCurrentScouting JSON request.")
 	flag.Parse()
 
 	spew.Config.Indent = *indentPtr
@@ -144,4 +146,11 @@
 		*requestAllNotesPtr,
 		*addressPtr,
 		debug.RequestAllNotes)
+
+	maybePerformRequest(
+		"requestCurrentScouting",
+		"scouting/webserver/requests/messages/request_current_scouting.fbs",
+		*requestCurrentScoutingPtr,
+		*addressPtr,
+		debug.RequestCurrentScouting)
 }
diff --git a/scouting/webserver/requests/debug/debug.go b/scouting/webserver/requests/debug/debug.go
index ed8a1b7..88b4eac 100644
--- a/scouting/webserver/requests/debug/debug.go
+++ b/scouting/webserver/requests/debug/debug.go
@@ -18,6 +18,7 @@
 	"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_all_notes_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_pit_images_response"
+	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_current_scouting_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_pit_images_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_shift_schedule_response"
@@ -33,6 +34,9 @@
 // The username to submit the various requests as.
 const DefaultUsername = "debug_cli"
 
+// TODO: Make the username an argument of the Request* functions.
+var Username = DefaultUsername
+
 // 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.
 type ResponseError struct {
@@ -82,7 +86,7 @@
 		return nil, err
 	}
 	req.Header.Add("Authorization", "Basic "+
-		base64.StdEncoding.EncodeToString([]byte(DefaultUsername+":")))
+		base64.StdEncoding.EncodeToString([]byte(Username+":")))
 
 	client := &http.Client{}
 	resp, err := client.Do(req)
@@ -172,6 +176,12 @@
 		request_shift_schedule_response.GetRootAsRequestShiftScheduleResponse)
 }
 
+func RequestCurrentScouting(server string, requestBytes []byte) (*request_current_scouting_response.RequestCurrentScoutingResponseT, error) {
+	return sendMessage[request_current_scouting_response.RequestCurrentScoutingResponseT](
+		server+"/requests/request/current_scouting", requestBytes,
+		request_current_scouting_response.GetRootAsRequestCurrentScoutingResponse)
+}
+
 func SubmitShiftSchedule(server string, requestBytes []byte) (*submit_shift_schedule_response.SubmitShiftScheduleResponseT, error) {
 	return sendMessage[submit_shift_schedule_response.SubmitShiftScheduleResponseT](
 		server+"/requests/submit/shift_schedule", requestBytes,
diff --git a/scouting/webserver/requests/messages/BUILD b/scouting/webserver/requests/messages/BUILD
index 6dee54d..3a36304 100644
--- a/scouting/webserver/requests/messages/BUILD
+++ b/scouting/webserver/requests/messages/BUILD
@@ -10,6 +10,8 @@
     "request_all_matches_response",
     "request_all_notes",
     "request_all_notes_response",
+    "request_current_scouting",
+    "request_current_scouting_response",
     "request_2023_data_scouting",
     "request_2023_data_scouting_response",
     "request_2024_data_scouting",
diff --git a/scouting/webserver/requests/messages/request_current_scouting.fbs b/scouting/webserver/requests/messages/request_current_scouting.fbs
new file mode 100644
index 0000000..9f3ef9c
--- /dev/null
+++ b/scouting/webserver/requests/messages/request_current_scouting.fbs
@@ -0,0 +1,7 @@
+namespace scouting.webserver.requests;
+
+table RequestCurrentScouting {
+	team_number:string (id: 0);
+}
+
+root_type RequestCurrentScouting;
diff --git a/scouting/webserver/requests/messages/request_current_scouting_response.fbs b/scouting/webserver/requests/messages/request_current_scouting_response.fbs
new file mode 100644
index 0000000..508fa46
--- /dev/null
+++ b/scouting/webserver/requests/messages/request_current_scouting_response.fbs
@@ -0,0 +1,10 @@
+namespace scouting.webserver.requests;
+
+table CollectedBy {
+	name: string (id: 0);
+}
+table RequestCurrentScoutingResponse {
+	collected_by:[CollectedBy] (id: 0);
+}
+
+root_type RequestCurrentScoutingResponse;
diff --git a/scouting/webserver/requests/requests.go b/scouting/webserver/requests/requests.go
index 0367ff2..321f224 100644
--- a/scouting/webserver/requests/requests.go
+++ b/scouting/webserver/requests/requests.go
@@ -10,6 +10,7 @@
 	"sort"
 	"strconv"
 	"strings"
+	"time"
 
 	"github.com/frc971/971-Robot-Code/scouting/db"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/delete_2023_data_scouting"
@@ -29,6 +30,8 @@
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_notes_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_pit_images"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_pit_images_response"
+	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_current_scouting"
+	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_current_scouting_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_pit_images"
@@ -69,6 +72,8 @@
 type RequestPitImagesResponseT = request_pit_images_response.RequestPitImagesResponseT
 type RequestAllPitImages = request_all_pit_images.RequestAllPitImages
 type RequestAllPitImagesResponseT = request_all_pit_images_response.RequestAllPitImagesResponseT
+type RequestCurrentScouting = request_current_scouting.RequestCurrentScouting
+type RequestCurrentScoutingResponseT = request_current_scouting_response.RequestCurrentScoutingResponseT
 type RequestNotesForTeam = request_notes_for_team.RequestNotesForTeam
 type RequestNotesForTeamResponseT = request_notes_for_team_response.RequestNotesForTeamResponseT
 type RequestShiftSchedule = request_shift_schedule.RequestShiftSchedule
@@ -116,6 +121,16 @@
 	DeleteFromActions(string, int32, int32, string) error
 }
 
+type Clock interface {
+	Now() time.Time
+}
+
+type RealClock struct{}
+
+func (RealClock) Now() time.Time {
+	return time.Now()
+}
+
 // Handles unknown requests. Just returns a 404.
 func unknown(w http.ResponseWriter, req *http.Request) {
 	w.WriteHeader(http.StatusNotFound)
@@ -372,6 +387,56 @@
 	w.Write(builder.FinishedBytes())
 }
 
+type requestCurrentScoutingHandler struct {
+	// Map that has a key of team number with a value is a map of names to timestamps
+	// so there aren't duplicate timestamps for one person.
+	scoutingMap map[string]map[string]time.Time
+	db          Database
+	clock       Clock
+}
+
+func (handler requestCurrentScoutingHandler) 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, "RequestCurrentScouting", request_current_scouting.GetRootAsRequestCurrentScouting)
+	if !success {
+		return
+	}
+	currentTime := handler.clock.Now()
+	teamNumber := string(request.TeamNumber())
+	collectedBy := parseUsername(req)
+
+	if handler.scoutingMap[teamNumber] == nil {
+		handler.scoutingMap[teamNumber] = map[string]time.Time{}
+	}
+	handler.scoutingMap[teamNumber][collectedBy] = currentTime
+	// Delete any scout information from 10+ seconds ago.
+	for team, teamMap := range handler.scoutingMap {
+		for name, timeStamp := range teamMap {
+			if currentTime.Sub(timeStamp) >= 10*time.Second {
+				delete(handler.scoutingMap[team], name)
+			}
+		}
+	}
+
+	var response RequestCurrentScoutingResponseT
+	for name, _ := range handler.scoutingMap[teamNumber] {
+		if name != collectedBy {
+			response.CollectedBy = append(response.CollectedBy, &request_current_scouting_response.CollectedByT{
+				Name: name,
+			})
+		}
+	}
+
+	builder := flatbuffers.NewBuilder(10)
+	builder.Finish((&response).Pack(builder))
+	w.Write(builder.FinishedBytes())
+}
+
 type submitNoteScoutingHandler struct {
 	db Database
 }
@@ -1299,7 +1364,7 @@
 	w.Write(builder.FinishedBytes())
 }
 
-func HandleRequests(db Database, scoutingServer server.ScoutingServer) {
+func HandleRequests(db Database, scoutingServer server.ScoutingServer, clock Clock) {
 	scoutingServer.HandleFunc("/requests", unknown)
 	scoutingServer.Handle("/requests/request/all_matches", requestAllMatchesHandler{db})
 	scoutingServer.Handle("/requests/request/all_notes", requestAllNotesHandler{db})
@@ -1310,6 +1375,7 @@
 	scoutingServer.Handle("/requests/submit/submit_pit_image", submitPitImageScoutingHandler{db})
 	scoutingServer.Handle("/requests/request/pit_images", requestPitImagesHandler{db})
 	scoutingServer.Handle("/requests/request/all_pit_images", requestAllPitImagesHandler{db})
+	scoutingServer.Handle("/requests/request/current_scouting", requestCurrentScoutingHandler{make(map[string]map[string]time.Time), db, clock})
 	scoutingServer.Handle("/requests/request/notes_for_team", requestNotesForTeamHandler{db})
 	scoutingServer.Handle("/requests/submit/shift_schedule", submitShiftScheduleHandler{db})
 	scoutingServer.Handle("/requests/request/shift_schedule", requestShiftScheduleHandler{db})
diff --git a/scouting/webserver/requests/requests_test.go b/scouting/webserver/requests/requests_test.go
index d0e17b8..19bcae4 100644
--- a/scouting/webserver/requests/requests_test.go
+++ b/scouting/webserver/requests/requests_test.go
@@ -4,6 +4,7 @@
 	"net/http"
 	"reflect"
 	"testing"
+	"time"
 
 	"github.com/frc971/971-Robot-Code/scouting/db"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/debug"
@@ -20,6 +21,8 @@
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_notes_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_pit_images"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_pit_images_response"
+	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_current_scouting"
+	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_current_scouting_response"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_pit_images"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_pit_images_response"
@@ -35,11 +38,20 @@
 	flatbuffers "github.com/google/flatbuffers/go"
 )
 
+type MockClock struct {
+	now time.Time
+}
+
+func (mockClock MockClock) Now() time.Time {
+	return mockClock.now
+}
+
 // Validates that an unhandled address results in a 404.
 func Test404(t *testing.T) {
 	db := MockDatabase{}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&db, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&db, scoutingServer, &mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -150,7 +162,8 @@
 		},
 	}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&db, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&db, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -235,7 +248,8 @@
 		},
 	}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&db, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&db, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -316,7 +330,8 @@
 		},
 	}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&db, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&db, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -542,7 +557,8 @@
 func TestSubmitNotes(t *testing.T) {
 	database := MockDatabase{}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&database, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&database, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -591,6 +607,81 @@
 	}
 }
 
+// Validates that we can request names of peoples who are currently scouting the same team.
+func TestRequestCurrentScouting(t *testing.T) {
+	database := MockDatabase{}
+	scoutingServer := server.NewScoutingServer()
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&database, scoutingServer, mockClock)
+	scoutingServer.Start(8080)
+	defer scoutingServer.Stop()
+
+	builder := flatbuffers.NewBuilder(1024)
+	builder.Finish((&request_current_scouting.RequestCurrentScoutingT{
+		TeamNumber: "971",
+	}).Pack(builder))
+	response, err := debug.RequestCurrentScouting("http://localhost:8080", builder.FinishedBytes())
+	if err != nil {
+		t.Fatal("Failed to request current scouting: ", err)
+	}
+
+	expected := request_current_scouting_response.RequestCurrentScoutingResponseT{
+		CollectedBy: []*request_current_scouting_response.CollectedByT{},
+	}
+
+	if len(expected.CollectedBy) != len(response.CollectedBy) {
+		t.Fatal("Expected ", expected, ", but got ", *response)
+	}
+	for i, collectRecord := range expected.CollectedBy {
+		if !reflect.DeepEqual(*collectRecord, *response.CollectedBy[i]) {
+			t.Fatal("Expected for collected by ", i, ":", *collectRecord, ", but got:", *response.CollectedBy[i])
+		}
+	}
+
+	debug.Username = "george"
+	builder.Finish((&request_current_scouting.RequestCurrentScoutingT{
+		TeamNumber: "971",
+	}).Pack(builder))
+	response, err = debug.RequestCurrentScouting("http://localhost:8080", builder.FinishedBytes())
+	if err != nil {
+		t.Fatal("Failed to request current scouting: ", err)
+	}
+
+	expected = request_current_scouting_response.RequestCurrentScoutingResponseT{
+		CollectedBy: []*request_current_scouting_response.CollectedByT{
+			{"debug_cli"},
+		},
+	}
+
+	if len(expected.CollectedBy) != len(response.CollectedBy) {
+		t.Fatal("Expected ", expected, ", but got ", *response)
+	}
+
+	for i, collectRecord := range expected.CollectedBy {
+		if !reflect.DeepEqual(*collectRecord, *response.CollectedBy[i]) {
+			t.Fatal("Expected for collected by ", i, ":", *collectRecord, ", but got:", *response.CollectedBy[i])
+		}
+	}
+
+	// After skipping 10 seconds ahead, the previous request from "debug_cli" should no longer appear.
+	mockClock.now = mockClock.now.Add(time.Second * 10)
+
+	builder.Finish((&request_current_scouting.RequestCurrentScoutingT{
+		TeamNumber: "971",
+	}).Pack(builder))
+	response, err = debug.RequestCurrentScouting("http://localhost:8080", builder.FinishedBytes())
+	if err != nil {
+		t.Fatal("Failed to request current scouting: ", err)
+	}
+
+	expected = request_current_scouting_response.RequestCurrentScoutingResponseT{
+		CollectedBy: []*request_current_scouting_response.CollectedByT{},
+	}
+
+	// Reset username for other tests.
+	debug.Username = "debug_cli"
+}
+
 func TestRequestNotes(t *testing.T) {
 	database := MockDatabase{
 		notes: []db.NotesData{{
@@ -610,7 +701,8 @@
 		}},
 	}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&database, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&database, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -631,7 +723,8 @@
 func TestSubmitPitImage(t *testing.T) {
 	database := MockDatabase{}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&database, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&database, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -677,7 +770,8 @@
 	}
 
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&db, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&db, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -727,7 +821,8 @@
 	}
 
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&db, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&db, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -788,7 +883,8 @@
 		},
 	}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&db, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&db, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -835,7 +931,8 @@
 func TestSubmitShiftSchedule(t *testing.T) {
 	database := MockDatabase{}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&database, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&database, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -874,7 +971,8 @@
 func TestSubmitDriverRanking(t *testing.T) {
 	database := MockDatabase{}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&database, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&database, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -919,7 +1017,8 @@
 		},
 	}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&db, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&db, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -994,7 +1093,8 @@
 		},
 	}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&db, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&db, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -1059,7 +1159,8 @@
 func TestAddingActions2024(t *testing.T) {
 	database := MockDatabase{}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&database, scoutingServer)
+	mockClock := MockClock{now: time.Now()}
+	HandleRequests(&database, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -1142,6 +1243,7 @@
 
 // Validates that we can delete 2024 stats.
 func TestDeleteFromStats2024(t *testing.T) {
+	mockClock := MockClock{now: time.Now()}
 	database := MockDatabase{
 		stats2024: []db.Stats2024{
 			{
@@ -1185,7 +1287,7 @@
 		},
 	}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(&database, scoutingServer)
+	HandleRequests(&database, scoutingServer, mockClock)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()