Add parsed driver ranking scores to the scouting database

This is the data that Michael's DriverRank.jl script generates. This
patch only prepares the database for the information. A future patch
will actually insert the data.

Signed-off-by: Philipp Schrader <philipp.schrader@gmail.com>
Change-Id: I0c4126bce3ad314734327156c08af8faeb8cbd7d
diff --git a/scouting/db/db.go b/scouting/db/db.go
index 162f973..eecc01c 100644
--- a/scouting/db/db.go
+++ b/scouting/db/db.go
@@ -125,6 +125,17 @@
 	Rank3       int32
 }
 
+type ParsedDriverRankingData struct {
+	// This data stores the output of DriverRank.jl.
+
+	TeamNumber string `gorm:"primaryKey"`
+
+	// The score of the team. A difference of 100 in two team's scores
+	// indicates that one team will outperform the other in 90% of the
+	// matches.
+	Score float32
+}
+
 // Opens a database at the specified port on localhost. We currently don't
 // support connecting to databases on other hosts.
 func NewDatabase(user string, password string, port int) (*Database, error) {
@@ -140,7 +151,7 @@
 		return nil, errors.New(fmt.Sprint("Failed to connect to postgres: ", err))
 	}
 
-	err = database.AutoMigrate(&TeamMatch{}, &Shift{}, &Stats{}, &Stats2023{}, &Action{}, &NotesData{}, &Ranking{}, &DriverRankingData{})
+	err = database.AutoMigrate(&TeamMatch{}, &Shift{}, &Stats{}, &Stats2023{}, &Action{}, &NotesData{}, &Ranking{}, &DriverRankingData{}, &ParsedDriverRankingData{})
 	if err != nil {
 		database.Delete()
 		return nil, errors.New(fmt.Sprint("Failed to create/migrate tables: ", err))
@@ -265,6 +276,12 @@
 	return rankings, result.Error
 }
 
+func (database *Database) ReturnAllParsedDriverRankings() ([]ParsedDriverRankingData, error) {
+	var rankings []ParsedDriverRankingData
+	result := database.Find(&rankings)
+	return rankings, result.Error
+}
+
 func (database *Database) ReturnAllShifts() ([]Shift, error) {
 	var shifts []Shift
 	result := database.Find(&shifts)
@@ -400,6 +417,13 @@
 	return result.Error
 }
 
+func (database *Database) AddParsedDriverRanking(data ParsedDriverRankingData) error {
+	result := database.Clauses(clause.OnConflict{
+		UpdateAll: true,
+	}).Create(&data)
+	return result.Error
+}
+
 func (database *Database) QueryDriverRanking(MatchNumber int) ([]DriverRankingData, error) {
 	var data []DriverRankingData
 	result := database.Where("match_number = ?", MatchNumber).Find(&data)
diff --git a/scouting/db/db_test.go b/scouting/db/db_test.go
index ec7a1e7..477a0f1 100644
--- a/scouting/db/db_test.go
+++ b/scouting/db/db_test.go
@@ -972,3 +972,44 @@
 		t.Errorf("Got %#v,\nbut expected %#v.", actual, expected)
 	}
 }
+
+func TestParsedDriverRanking(t *testing.T) {
+	fixture := createDatabase(t)
+	defer fixture.TearDown()
+
+	expected := []ParsedDriverRankingData{
+		{TeamNumber: "1234", Score: 100},
+		{TeamNumber: "1235", Score: 110},
+		{TeamNumber: "1236", Score: 90},
+	}
+
+	for i := range expected {
+		err := fixture.db.AddParsedDriverRanking(expected[i])
+		check(t, err, "Failed to add Parsed Driver Ranking")
+	}
+
+	actual, err := fixture.db.ReturnAllParsedDriverRankings()
+	check(t, err, "Failed to get Parsed Driver Ranking")
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Got %#v,\nbut expected %#v.", actual, expected)
+	}
+
+	// Now update one of the rankings and make sure we get the properly
+	// merged result.
+	err = fixture.db.AddParsedDriverRanking(ParsedDriverRankingData{
+		TeamNumber: "1235", Score: 200,
+	})
+	check(t, err, "Failed to add Parsed Driver Ranking")
+
+	expected = []ParsedDriverRankingData{
+		{TeamNumber: "1234", Score: 100},
+		{TeamNumber: "1236", Score: 90},
+		{TeamNumber: "1235", Score: 200},
+	}
+
+	actual, err = fixture.db.ReturnAllParsedDriverRankings()
+	check(t, err, "Failed to get Parsed Driver Ranking")
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Got %#v,\nbut expected %#v.", actual, expected)
+	}
+}