Convert EventSettings, Match, and MatchResult models to use Bolt DB.

This commit is contained in:
Patrick Fairbank
2021-05-12 16:40:07 -07:00
parent ea4d56e665
commit 1d523c5f37
28 changed files with 274 additions and 375 deletions

View File

@@ -15,12 +15,12 @@ type AllianceTeam struct {
}
func (database *Database) CreateAllianceTeam(allianceTeam *AllianceTeam) error {
return database.tables[AllianceTeam{}].create(allianceTeam)
return database.allianceTeamTable.create(allianceTeam)
}
func (database *Database) GetTeamsByAlliance(allianceId int) ([]AllianceTeam, error) {
var allianceTeams []AllianceTeam
if err := database.tables[AllianceTeam{}].getAll(&allianceTeams); err != nil {
if err := database.allianceTeamTable.getAll(&allianceTeams); err != nil {
return nil, err
}
sort.Slice(allianceTeams, func(i, j int) bool {
@@ -37,20 +37,20 @@ func (database *Database) GetTeamsByAlliance(allianceId int) ([]AllianceTeam, er
}
func (database *Database) UpdateAllianceTeam(allianceTeam *AllianceTeam) error {
return database.tables[AllianceTeam{}].update(allianceTeam)
return database.allianceTeamTable.update(allianceTeam)
}
func (database *Database) DeleteAllianceTeam(id int64) error {
return database.tables[AllianceTeam{}].delete(id)
return database.allianceTeamTable.delete(id)
}
func (database *Database) TruncateAllianceTeams() error {
return database.tables[AllianceTeam{}].truncate()
return database.allianceTeamTable.truncate()
}
func (database *Database) GetAllAlliances() ([][]AllianceTeam, error) {
var allianceTeams []AllianceTeam
if err := database.tables[AllianceTeam{}].getAll(&allianceTeams); err != nil {
if err := database.allianceTeamTable.getAll(&allianceTeams); err != nil {
return nil, err
}
sort.Slice(allianceTeams, func(i, j int) bool {

View File

@@ -24,30 +24,30 @@ const (
)
func (database *Database) CreateAward(award *Award) error {
return database.tables[Award{}].create(award)
return database.awardTable.create(award)
}
func (database *Database) GetAwardById(id int64) (*Award, error) {
var award *Award
err := database.tables[Award{}].getById(id, &award)
err := database.awardTable.getById(id, &award)
return award, err
}
func (database *Database) UpdateAward(award *Award) error {
return database.tables[Award{}].update(award)
return database.awardTable.update(award)
}
func (database *Database) DeleteAward(id int64) error {
return database.tables[Award{}].delete(id)
return database.awardTable.delete(id)
}
func (database *Database) TruncateAwards() error {
return database.tables[Award{}].truncate()
return database.awardTable.truncate()
}
func (database *Database) GetAllAwards() ([]Award, error) {
var awards []Award
if err := database.tables[Award{}].getAll(&awards); err != nil {
if err := database.awardTable.getAll(&awards); err != nil {
return nil, err
}
sort.Slice(awards, func(i, j int) bool {

View File

@@ -24,25 +24,22 @@ const backupsDir = "db/backups"
const migrationsDir = "db/migrations"
var BaseDir = "." // Mutable for testing
var recordTypes = []interface{}{
AllianceTeam{},
Award{},
LowerThird{},
}
type Database struct {
Path string
db *sql.DB
eventSettingsMap *modl.DbMap
matchMap *modl.DbMap
matchResultMap *modl.DbMap
rankingMap *modl.DbMap
teamMap *modl.DbMap
sponsorSlideMap *modl.DbMap
scheduleBlockMap *modl.DbMap
userSessionMap *modl.DbMap
bolt *bbolt.DB
tables map[interface{}]*table
Path string
db *sql.DB
rankingMap *modl.DbMap
teamMap *modl.DbMap
sponsorSlideMap *modl.DbMap
scheduleBlockMap *modl.DbMap
userSessionMap *modl.DbMap
bolt *bbolt.DB
allianceTeamTable *table
awardTable *table
eventSettingsTable *table
lowerThirdTable *table
matchTable *table
matchResultTable *table
}
// Opens the SQLite database at the given path, creating it if it doesn't exist, and runs any pending
@@ -75,13 +72,23 @@ func OpenDatabase(filename string) (*Database, error) {
}
// Register tables.
database.tables = make(map[interface{}]*table)
for _, recordType := range recordTypes {
table, err := database.newTable(recordType)
if err != nil {
return nil, err
}
database.tables[recordType] = table
if database.allianceTeamTable, err = database.newTable(AllianceTeam{}); err != nil {
return nil, err
}
if database.awardTable, err = database.newTable(Award{}); err != nil {
return nil, err
}
if database.eventSettingsTable, err = database.newTable(EventSettings{}); err != nil {
return nil, err
}
if database.lowerThirdTable, err = database.newTable(LowerThird{}); err != nil {
return nil, err
}
if database.matchTable, err = database.newTable(Match{}); err != nil {
return nil, err
}
if database.matchResultTable, err = database.newTable(MatchResult{}); err != nil {
return nil, err
}
return &database, nil
@@ -121,15 +128,6 @@ func (database *Database) Backup(eventName, reason string) error {
func (database *Database) mapTables() {
dialect := new(modl.SqliteDialect)
database.eventSettingsMap = modl.NewDbMap(database.db, dialect)
database.eventSettingsMap.AddTableWithName(EventSettings{}, "event_settings").SetKeys(false, "Id")
database.matchMap = modl.NewDbMap(database.db, dialect)
database.matchMap.AddTableWithName(Match{}, "matches").SetKeys(true, "Id")
database.matchResultMap = modl.NewDbMap(database.db, dialect)
database.matchResultMap.AddTableWithName(MatchResultDb{}, "match_results").SetKeys(true, "Id")
database.rankingMap = modl.NewDbMap(database.db, dialect)
database.rankingMap.AddTableWithName(RankingDb{}, "rankings").SetKeys(false, "TeamId")

View File

@@ -8,7 +8,7 @@ package model
import "github.com/Team254/cheesy-arena-lite/game"
type EventSettings struct {
Id int
Id int64 `db:"id"`
Name string
NumElimAlliances int
SelectionRound2Order string
@@ -40,38 +40,39 @@ type EventSettings struct {
WarningRemainingDurationSec int
}
const eventSettingsId = 0
func (database *Database) GetEventSettings() (*EventSettings, error) {
eventSettings := new(EventSettings)
err := database.eventSettingsMap.Get(eventSettings, eventSettingsId)
if err != nil {
// Database record doesn't exist yet; create it now.
eventSettings.Name = "Untitled Event"
eventSettings.NumElimAlliances = 8
eventSettings.SelectionRound2Order = "L"
eventSettings.SelectionRound3Order = ""
eventSettings.TBADownloadEnabled = true
eventSettings.ApTeamChannel = 157
eventSettings.ApAdminChannel = 0
eventSettings.ApAdminWpaKey = "1234Five"
eventSettings.Ap2TeamChannel = 0
eventSettings.WarmupDurationSec = game.MatchTiming.WarmupDurationSec
eventSettings.AutoDurationSec = game.MatchTiming.AutoDurationSec
eventSettings.PauseDurationSec = game.MatchTiming.PauseDurationSec
eventSettings.TeleopDurationSec = game.MatchTiming.TeleopDurationSec
eventSettings.WarningRemainingDurationSec = game.MatchTiming.WarningRemainingDurationSec
err = database.eventSettingsMap.Insert(eventSettings)
if err != nil {
return nil, err
}
var allEventSettings []EventSettings
if err := database.eventSettingsTable.getAll(&allEventSettings); err != nil {
return nil, err
}
return eventSettings, nil
if len(allEventSettings) == 1 {
return &allEventSettings[0], nil
}
// Database record doesn't exist yet; create it now.
eventSettings := EventSettings{
Name: "Untitled Event",
NumElimAlliances: 8,
SelectionRound2Order: "L",
SelectionRound3Order: "",
TBADownloadEnabled: true,
ApTeamChannel: 157,
ApAdminChannel: 0,
ApAdminWpaKey: "1234Five",
Ap2TeamChannel: 0,
WarmupDurationSec: game.MatchTiming.WarmupDurationSec,
AutoDurationSec: game.MatchTiming.AutoDurationSec,
PauseDurationSec: game.MatchTiming.PauseDurationSec,
TeleopDurationSec: game.MatchTiming.TeleopDurationSec,
WarningRemainingDurationSec: game.MatchTiming.WarningRemainingDurationSec,
}
if err := database.eventSettingsTable.create(&eventSettings); err != nil {
return nil, err
}
return &eventSettings, nil
}
func (database *Database) SaveEventSettings(eventSettings *EventSettings) error {
eventSettings.Id = eventSettingsId
_, err := database.eventSettingsMap.Update(eventSettings)
return err
func (database *Database) UpdateEventSettings(eventSettings *EventSettings) error {
return database.eventSettingsTable.update(eventSettings)
}

View File

@@ -14,7 +14,7 @@ func TestEventSettingsReadWrite(t *testing.T) {
eventSettings, err := db.GetEventSettings()
assert.Nil(t, err)
assert.Equal(t, EventSettings{Id: 0, Name: "Untitled Event", NumElimAlliances: 8, SelectionRound2Order: "L",
assert.Equal(t, EventSettings{Id: 1, Name: "Untitled Event", NumElimAlliances: 8, SelectionRound2Order: "L",
SelectionRound3Order: "", TBADownloadEnabled: true, ApTeamChannel: 157, ApAdminChannel: 0,
ApAdminWpaKey: "1234Five", WarmupDurationSec: 0, AutoDurationSec: 15, PauseDurationSec: 2,
TeleopDurationSec: 135, WarningRemainingDurationSec: 30}, *eventSettings)
@@ -23,7 +23,7 @@ func TestEventSettingsReadWrite(t *testing.T) {
eventSettings.NumElimAlliances = 6
eventSettings.SelectionRound2Order = "F"
eventSettings.SelectionRound3Order = "L"
err = db.SaveEventSettings(eventSettings)
err = db.UpdateEventSettings(eventSettings)
assert.Nil(t, err)
eventSettings2, err := db.GetEventSettings()
assert.Nil(t, err)

View File

@@ -18,30 +18,30 @@ type LowerThird struct {
}
func (database *Database) CreateLowerThird(lowerThird *LowerThird) error {
return database.tables[LowerThird{}].create(lowerThird)
return database.lowerThirdTable.create(lowerThird)
}
func (database *Database) GetLowerThirdById(id int64) (*LowerThird, error) {
var lowerThird *LowerThird
err := database.tables[LowerThird{}].getById(id, &lowerThird)
err := database.lowerThirdTable.getById(id, &lowerThird)
return lowerThird, err
}
func (database *Database) UpdateLowerThird(lowerThird *LowerThird) error {
return database.tables[LowerThird{}].update(lowerThird)
return database.lowerThirdTable.update(lowerThird)
}
func (database *Database) DeleteLowerThird(id int64) error {
return database.tables[LowerThird{}].delete(id)
return database.lowerThirdTable.delete(id)
}
func (database *Database) TruncateLowerThirds() error {
return database.tables[LowerThird{}].truncate()
return database.lowerThirdTable.truncate()
}
func (database *Database) GetAllLowerThirds() ([]LowerThird, error) {
var lowerThirds []LowerThird
if err := database.tables[LowerThird{}].getAll(&lowerThirds); err != nil {
if err := database.lowerThirdTable.getAll(&lowerThirds); err != nil {
return nil, err
}
sort.Slice(lowerThirds, func(i, j int) bool {

View File

@@ -7,12 +7,13 @@ package model
import (
"fmt"
"sort"
"strings"
"time"
)
type Match struct {
Id int
Id int64 `db:"id"`
Type string
DisplayName string
Time time.Time
@@ -50,58 +51,82 @@ const (
var ElimRoundNames = map[int]string{1: "F", 2: "SF", 4: "QF", 8: "EF"}
func (database *Database) CreateMatch(match *Match) error {
return database.matchMap.Insert(match)
return database.matchTable.create(match)
}
func (database *Database) GetMatchById(id int) (*Match, error) {
match := new(Match)
err := database.matchMap.Get(match, id)
if err != nil && err.Error() == "sql: no rows in result set" {
match = nil
err = nil
}
func (database *Database) GetMatchById(id int64) (*Match, error) {
var match *Match
err := database.matchTable.getById(id, &match)
return match, err
}
func (database *Database) SaveMatch(match *Match) error {
_, err := database.matchMap.Update(match)
return err
func (database *Database) UpdateMatch(match *Match) error {
return database.matchTable.update(match)
}
func (database *Database) DeleteMatch(match *Match) error {
_, err := database.matchMap.Delete(match)
return err
func (database *Database) DeleteMatch(id int64) error {
return database.matchTable.delete(id)
}
func (database *Database) TruncateMatches() error {
return database.matchMap.TruncateTables()
return database.matchTable.truncate()
}
func (database *Database) GetMatchByName(matchType string, displayName string) (*Match, error) {
var matches []Match
err := database.matchMap.Select(&matches, "SELECT * FROM matches WHERE type = ? AND displayname = ?",
matchType, displayName)
if err != nil {
if err := database.matchTable.getAll(&matches); err != nil {
return nil, err
}
if len(matches) == 0 {
return nil, nil
for _, match := range matches {
if match.Type == matchType && match.DisplayName == displayName {
return &match, nil
}
}
return &matches[0], err
return nil, nil
}
func (database *Database) GetMatchesByElimRoundGroup(round int, group int) ([]Match, error) {
var matches []Match
err := database.matchMap.Select(&matches, "SELECT * FROM matches WHERE type = 'elimination' AND "+
"elimround = ? AND elimgroup = ? ORDER BY eliminstance", round, group)
return matches, err
matches, err := database.GetMatchesByType("elimination")
if err != nil {
return nil, err
}
var matchingMatches []Match
for _, match := range matches {
if match.ElimRound == round && match.ElimGroup == group {
matchingMatches = append(matchingMatches, match)
}
}
return matchingMatches, nil
}
func (database *Database) GetMatchesByType(matchType string) ([]Match, error) {
var matches []Match
err := database.matchMap.Select(&matches,
"SELECT * FROM matches WHERE type = ? ORDER BY elimround desc, eliminstance, elimgroup, id", matchType)
return matches, err
if err := database.matchTable.getAll(&matches); err != nil {
return nil, err
}
var matchingMatches []Match
for _, match := range matches {
if match.Type == matchType {
matchingMatches = append(matchingMatches, match)
}
}
sort.Slice(matchingMatches, func(i, j int) bool {
if matchingMatches[i].ElimRound == matchingMatches[j].ElimRound {
if matchingMatches[i].ElimInstance == matchingMatches[j].ElimInstance {
if matchingMatches[i].ElimGroup == matchingMatches[j].ElimGroup {
return matchingMatches[i].Id < matchingMatches[j].Id
}
return matchingMatches[i].ElimGroup < matchingMatches[j].ElimGroup
}
return matchingMatches[i].ElimInstance < matchingMatches[j].ElimInstance
}
return matchingMatches[i].ElimRound > matchingMatches[j].ElimRound
})
return matchingMatches, nil
}
func (match *Match) IsComplete() bool {

View File

@@ -6,28 +6,18 @@
package model
import (
"encoding/json"
"github.com/Team254/cheesy-arena-lite/game"
)
type MatchResult struct {
Id int
MatchId int
Id int64 `db:"id"`
MatchId int64
PlayNumber int
MatchType string
RedScore *game.Score
BlueScore *game.Score
}
type MatchResultDb struct {
Id int
MatchId int
PlayNumber int
MatchType string
RedScoreJson string
BlueScoreJson string
}
// Returns a new match result object with empty slices instead of nil.
func NewMatchResult() *MatchResult {
matchResult := new(MatchResult)
@@ -37,55 +27,35 @@ func NewMatchResult() *MatchResult {
}
func (database *Database) CreateMatchResult(matchResult *MatchResult) error {
matchResultDb, err := matchResult.Serialize()
if err != nil {
return err
}
err = database.matchResultMap.Insert(matchResultDb)
if err != nil {
return err
}
matchResult.Id = matchResultDb.Id
return nil
return database.matchResultTable.create(matchResult)
}
func (database *Database) GetMatchResultForMatch(matchId int) (*MatchResult, error) {
var matchResults []MatchResultDb
query := "SELECT * FROM match_results WHERE matchid = ? ORDER BY playnumber DESC LIMIT 1"
err := database.matchResultMap.Select(&matchResults, query, matchId)
if err != nil {
func (database *Database) GetMatchResultForMatch(matchId int64) (*MatchResult, error) {
var matchResults []MatchResult
if err := database.matchResultTable.getAll(&matchResults); err != nil {
return nil, err
}
if len(matchResults) == 0 {
return nil, nil
var mostRecentMatchResult *MatchResult
for i, matchResult := range matchResults {
if matchResult.MatchId == matchId &&
(mostRecentMatchResult == nil || matchResult.PlayNumber > mostRecentMatchResult.PlayNumber) {
mostRecentMatchResult = &matchResults[i]
}
}
matchResult, err := matchResults[0].Deserialize()
if err != nil {
return nil, err
}
return matchResult, err
return mostRecentMatchResult, nil
}
func (database *Database) SaveMatchResult(matchResult *MatchResult) error {
matchResultDb, err := matchResult.Serialize()
if err != nil {
return err
}
_, err = database.matchResultMap.Update(matchResultDb)
return err
func (database *Database) UpdateMatchResult(matchResult *MatchResult) error {
return database.matchResultTable.update(matchResult)
}
func (database *Database) DeleteMatchResult(matchResult *MatchResult) error {
matchResultDb, err := matchResult.Serialize()
if err != nil {
return err
}
_, err = database.matchResultMap.Delete(matchResultDb)
return err
func (database *Database) DeleteMatchResult(id int64) error {
return database.matchResultTable.delete(id)
}
func (database *Database) TruncateMatchResults() error {
return database.matchResultMap.TruncateTables()
return database.matchResultTable.truncate()
}
// Calculates and returns the summary fields used for ranking and display for the red alliance.
@@ -97,29 +67,3 @@ func (matchResult *MatchResult) RedScoreSummary() *game.ScoreSummary {
func (matchResult *MatchResult) BlueScoreSummary() *game.ScoreSummary {
return matchResult.BlueScore.Summarize()
}
// Converts the nested struct MatchResult to the DB version that has JSON fields.
func (matchResult *MatchResult) Serialize() (*MatchResultDb, error) {
matchResultDb := MatchResultDb{Id: matchResult.Id, MatchId: matchResult.MatchId,
PlayNumber: matchResult.PlayNumber, MatchType: matchResult.MatchType}
if err := serializeHelper(&matchResultDb.RedScoreJson, matchResult.RedScore); err != nil {
return nil, err
}
if err := serializeHelper(&matchResultDb.BlueScoreJson, matchResult.BlueScore); err != nil {
return nil, err
}
return &matchResultDb, nil
}
// Converts the DB MatchResult with JSON fields to the nested struct version.
func (matchResultDb *MatchResultDb) Deserialize() (*MatchResult, error) {
matchResult := MatchResult{Id: matchResultDb.Id, MatchId: matchResultDb.MatchId,
PlayNumber: matchResultDb.PlayNumber, MatchType: matchResultDb.MatchType}
if err := json.Unmarshal([]byte(matchResultDb.RedScoreJson), &matchResult.RedScore); err != nil {
return nil, err
}
if err := json.Unmarshal([]byte(matchResultDb.BlueScoreJson), &matchResult.BlueScore); err != nil {
return nil, err
}
return &matchResult, nil
}

View File

@@ -27,12 +27,13 @@ func TestMatchResultCrud(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, matchResult, matchResult2)
db.SaveMatchResult(matchResult)
matchResult.BlueScore.EndgamePoints = 1234
assert.Nil(t, db.UpdateMatchResult(matchResult))
matchResult2, err = db.GetMatchResultForMatch(254)
assert.Nil(t, err)
assert.Equal(t, matchResult, matchResult2)
db.DeleteMatchResult(matchResult)
assert.Nil(t, db.DeleteMatchResult(matchResult.Id))
matchResult2, err = db.GetMatchResultForMatch(254)
assert.Nil(t, err)
assert.Nil(t, matchResult2)
@@ -43,8 +44,8 @@ func TestTruncateMatchResults(t *testing.T) {
defer db.Close()
matchResult := BuildTestMatchResult(254, 1)
db.CreateMatchResult(matchResult)
db.TruncateMatchResults()
assert.Nil(t, db.CreateMatchResult(matchResult))
assert.Nil(t, db.TruncateMatchResults())
matchResult2, err := db.GetMatchResultForMatch(254)
assert.Nil(t, err)
assert.Nil(t, matchResult2)
@@ -55,11 +56,11 @@ func TestGetMatchResultForMatch(t *testing.T) {
defer db.Close()
matchResult := BuildTestMatchResult(254, 2)
db.CreateMatchResult(matchResult)
assert.Nil(t, db.CreateMatchResult(matchResult))
matchResult2 := BuildTestMatchResult(254, 5)
db.CreateMatchResult(matchResult2)
assert.Nil(t, db.CreateMatchResult(matchResult2))
matchResult3 := BuildTestMatchResult(254, 4)
db.CreateMatchResult(matchResult3)
assert.Nil(t, db.CreateMatchResult(matchResult3))
// Should return the match result with the highest play number (i.e. the most recent).
matchResult4, err := db.GetMatchResultForMatch(254)

View File

@@ -33,12 +33,12 @@ func TestMatchCrud(t *testing.T) {
assert.Equal(t, match, *match3)
match.Status = RedWonMatch
db.SaveMatch(&match)
db.UpdateMatch(&match)
match2, err = db.GetMatchById(1)
assert.Nil(t, err)
assert.Equal(t, match.Status, match2.Status)
db.DeleteMatch(&match)
db.DeleteMatch(match.Id)
match2, err = db.GetMatchById(1)
assert.Nil(t, err)
assert.Nil(t, match2)

View File

@@ -24,7 +24,7 @@ func SetupTestDb(t *testing.T, uniqueName string) *Database {
return database
}
func BuildTestMatchResult(matchId int, playNumber int) *MatchResult {
func BuildTestMatchResult(matchId int64, playNumber int) *MatchResult {
matchResult := &MatchResult{MatchId: matchId, PlayNumber: playNumber, MatchType: "qualification"}
matchResult.RedScore = game.TestScore1()
matchResult.BlueScore = game.TestScore2()