blob: 031f54cabe983bc5a925f664c5a9d436a20e44d7 [file] [log] [blame]
Sabina Leaverc5fd2772022-01-29 17:00:23 -08001package db
2
3import (
Philipp Schrader30005e42022-03-06 13:53:58 -08004 "errors"
Sabina Leaverc5fd2772022-01-29 17:00:23 -08005 "fmt"
Philipp Schradereecb8962022-06-01 21:02:42 -07006 "gorm.io/driver/postgres"
7 "gorm.io/gorm"
8 "gorm.io/gorm/clause"
9 "gorm.io/gorm/logger"
Sabina Leaverc5fd2772022-01-29 17:00:23 -080010)
11
12type Database struct {
Philipp Schradereecb8962022-06-01 21:02:42 -070013 *gorm.DB
Sabina Leaverc5fd2772022-01-29 17:00:23 -080014}
15
Emily Markovabf24c9e2023-02-08 20:31:11 -080016type TeamMatch struct {
17 MatchNumber int32 `gorm:"primaryKey"`
18 SetNumber int32 `gorm:"primaryKey"`
19 CompLevel string `gorm:"primaryKey"`
20 Alliance string `gorm:"primaryKey"` // "R" or "B"
21 AlliancePosition int32 `gorm:"primaryKey"` // 1, 2, or 3
Emily Markovab8551572023-03-22 19:49:39 -070022 TeamNumber string
Sabina Leaverc5fd2772022-01-29 17:00:23 -080023}
24
Milo Lina72e2002022-04-06 20:31:13 -070025type Shift struct {
Philipp Schradereecb8962022-06-01 21:02:42 -070026 MatchNumber int32 `gorm:"primaryKey"`
Milo Lina72e2002022-04-06 20:31:13 -070027 R1scouter, R2scouter, R3scouter, B1scouter, B2scouter, B3scouter string
28}
29
Sabina Leaver759090b2023-01-14 20:42:56 -080030type Stats2023 struct {
31 TeamNumber string `gorm:"primaryKey"`
32 MatchNumber int32 `gorm:"primaryKey"`
33 SetNumber int32 `gorm:"primaryKey"`
34 CompLevel string `gorm:"primaryKey"`
35 StartingQuadrant int32
36 LowCubesAuto, MiddleCubesAuto, HighCubesAuto, CubesDroppedAuto int32
37 LowConesAuto, MiddleConesAuto, HighConesAuto, ConesDroppedAuto int32
38 LowCubes, MiddleCubes, HighCubes, CubesDropped int32
39 LowCones, MiddleCones, HighCones, ConesDropped int32
Filip Kujawa7a045e72023-04-13 08:41:09 -070040 SuperchargedPieces int32
Philipp Schrader8c878a22023-03-20 22:36:38 -070041 AvgCycle int64
Filip Kujawa0b4b1e52023-04-15 14:05:40 -070042 Mobility bool
Emily Markova63c63f62023-03-29 20:57:35 -070043 DockedAuto, EngagedAuto, BalanceAttemptAuto bool
44 Docked, Engaged, BalanceAttempt bool
45
Sabina Leaver759090b2023-01-14 20:42:56 -080046 // The username of the person who collected these statistics.
47 // "unknown" if submitted without logging in.
48 // Empty if the stats have not yet been collected.
49 CollectedBy string
50}
51
52type Action struct {
Sabina Leaver9b4eb312023-02-20 19:58:17 -080053 TeamNumber string `gorm:"primaryKey"`
54 MatchNumber int32 `gorm:"primaryKey"`
55 SetNumber int32 `gorm:"primaryKey"`
56 CompLevel string `gorm:"primaryKey"`
Sabina Leaver759090b2023-01-14 20:42:56 -080057 // This contains a serialized scouting.webserver.requests.ActionType flatbuffer.
Sabina Leaver9b4eb312023-02-20 19:58:17 -080058 CompletedAction []byte
59 Timestamp int64 `gorm:"primaryKey"`
60 CollectedBy string
Sabina Leaver759090b2023-01-14 20:42:56 -080061}
62
Alex Perry871eab92022-03-12 17:43:52 -080063type NotesData struct {
Filip Kujawa7ddd5652023-03-07 19:56:15 -080064 ID uint `gorm:"primaryKey"`
65 TeamNumber int32
66 Notes string
67 GoodDriving bool
68 BadDriving bool
Filip Kujawa11dc4c92023-04-13 08:55:43 -070069 SolidPlacing bool
Filip Kujawa7ddd5652023-03-07 19:56:15 -080070 SketchyPlacing bool
71 GoodDefense bool
72 BadDefense bool
73 EasilyDefended bool
Alex Perry871eab92022-03-12 17:43:52 -080074}
75
Yash Chainanibcd1bb32022-04-02 17:10:24 -070076type Ranking struct {
Philipp Schradereecb8962022-06-01 21:02:42 -070077 TeamNumber int `gorm:"primaryKey"`
Yash Chainanibcd1bb32022-04-02 17:10:24 -070078 Losses, Wins, Ties int32
79 Rank, Dq int32
80}
81
Filip Kujawa210a03b2022-11-24 14:41:11 -080082type DriverRankingData struct {
83 // Each entry in the table is a single scout's ranking.
84 // Multiple scouts can submit a driver ranking for the same
85 // teams in the same match.
86 // The teams being ranked are stored in Rank1, Rank2, Rank3,
87 // Rank1 being the best driving and Rank3 being the worst driving.
88
89 ID uint `gorm:"primaryKey"`
90 MatchNumber int32
91 Rank1 int32
92 Rank2 int32
93 Rank3 int32
94}
95
Philipp Schradera8955fb2023-03-05 15:47:19 -080096type ParsedDriverRankingData struct {
97 // This data stores the output of DriverRank.jl.
98
99 TeamNumber string `gorm:"primaryKey"`
100
101 // The score of the team. A difference of 100 in two team's scores
102 // indicates that one team will outperform the other in 90% of the
103 // matches.
104 Score float32
105}
106
Philipp Schrader7365d322022-03-06 16:40:08 -0800107// Opens a database at the specified port on localhost. We currently don't
108// support connecting to databases on other hosts.
109func NewDatabase(user string, password string, port int) (*Database, error) {
Philipp Schrader83fc2722022-03-10 21:59:20 -0800110 var err error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800111 database := new(Database)
Philipp Schrader83fc2722022-03-10 21:59:20 -0800112
Philipp Schradereecb8962022-06-01 21:02:42 -0700113 dsn := fmt.Sprintf("host=localhost user=%s password=%s dbname=postgres port=%d sslmode=disable", user, password, port)
114 database.DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
115 Logger: logger.Default.LogMode(logger.Silent),
116 })
Philipp Schrader7365d322022-03-06 16:40:08 -0800117 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -0700118 database.Delete()
Philipp Schrader7365d322022-03-06 16:40:08 -0800119 return nil, errors.New(fmt.Sprint("Failed to connect to postgres: ", err))
120 }
Philipp Schrader36df73a2022-03-17 23:27:24 -0700121
Emily Markova132e5be2023-03-25 13:43:05 -0700122 err = database.AutoMigrate(&TeamMatch{}, &Shift{}, &Stats2023{}, &Action{}, &NotesData{}, &Ranking{}, &DriverRankingData{}, &ParsedDriverRankingData{})
Philipp Schrader83fc2722022-03-10 21:59:20 -0800123 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -0700124 database.Delete()
125 return nil, errors.New(fmt.Sprint("Failed to create/migrate tables: ", err))
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700126 }
127
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800128 return database, nil
129}
130
131func (database *Database) Delete() error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700132 sql, err := database.DB.DB()
Philipp Schrader83fc2722022-03-10 21:59:20 -0800133 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -0700134 return err
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800135 }
Philipp Schradereecb8962022-06-01 21:02:42 -0700136 return sql.Close()
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800137}
138
Philipp Schradereecb8962022-06-01 21:02:42 -0700139func (database *Database) SetDebugLogLevel() {
140 database.DB.Logger = database.DB.Logger.LogMode(logger.Info)
141}
Philipp Schradercd12c952022-04-08 18:58:49 -0700142
Emily Markovabf24c9e2023-02-08 20:31:11 -0800143func (database *Database) AddToMatch(m TeamMatch) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700144 result := database.Clauses(clause.OnConflict{
145 UpdateAll: true,
146 }).Create(&m)
147 return result.Error
Philipp Schradercd12c952022-04-08 18:58:49 -0700148}
149
Milo Lina72e2002022-04-06 20:31:13 -0700150func (database *Database) AddToShift(sh Shift) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700151 result := database.Clauses(clause.OnConflict{
152 UpdateAll: true,
153 }).Create(&sh)
154 return result.Error
Milo Lina72e2002022-04-06 20:31:13 -0700155}
156
Sabina Leaver759090b2023-01-14 20:42:56 -0800157func (database *Database) AddAction(a Action) error {
158 result := database.Clauses(clause.OnConflict{
159 UpdateAll: true,
160 }).Create(&a)
161 return result.Error
162}
163
Sabina Leaver759090b2023-01-14 20:42:56 -0800164func (database *Database) AddToStats2023(s Stats2023) error {
165 matches, err := database.QueryMatchesString(s.TeamNumber)
166 if err != nil {
167 return err
168 }
169 foundMatch := false
170 for _, match := range matches {
171 if match.MatchNumber == s.MatchNumber {
172 foundMatch = true
173 break
174 }
175 }
176 if !foundMatch {
177 return errors.New(fmt.Sprint(
178 "Failed to find team ", s.TeamNumber,
179 " in match ", s.MatchNumber, " in the schedule."))
180 }
181
182 result := database.Create(&s)
183 return result.Error
184}
185
Emily Markova6b551e02023-02-18 17:37:40 -0800186func (database *Database) DeleteFromStats(compLevel_ string, matchNumber_ int32, setNumber_ int32, teamNumber_ string) error {
187 var stats2023 []Stats2023
188 result := database.
189 Where("comp_level = ? AND match_number = ? AND set_number = ? AND team_number = ?", compLevel_, matchNumber_, setNumber_, teamNumber_).
190 Delete(&stats2023)
191 return result.Error
192}
193
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700194func (database *Database) AddOrUpdateRankings(r Ranking) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700195 result := database.Clauses(clause.OnConflict{
196 UpdateAll: true,
197 }).Create(&r)
198 return result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700199}
200
Emily Markovabf24c9e2023-02-08 20:31:11 -0800201func (database *Database) ReturnMatches() ([]TeamMatch, error) {
202 var matches []TeamMatch
Philipp Schradereecb8962022-06-01 21:02:42 -0700203 result := database.Find(&matches)
204 return matches, result.Error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800205}
206
Filip Kujawaf882e022022-12-14 13:14:08 -0800207func (database *Database) ReturnAllNotes() ([]NotesData, error) {
208 var notes []NotesData
209 result := database.Find(&notes)
210 return notes, result.Error
211}
212
213func (database *Database) ReturnAllDriverRankings() ([]DriverRankingData, error) {
214 var rankings []DriverRankingData
215 result := database.Find(&rankings)
216 return rankings, result.Error
217}
218
Philipp Schradera8955fb2023-03-05 15:47:19 -0800219func (database *Database) ReturnAllParsedDriverRankings() ([]ParsedDriverRankingData, error) {
220 var rankings []ParsedDriverRankingData
221 result := database.Find(&rankings)
222 return rankings, result.Error
223}
224
Milo Lina72e2002022-04-06 20:31:13 -0700225func (database *Database) ReturnAllShifts() ([]Shift, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700226 var shifts []Shift
227 result := database.Find(&shifts)
228 return shifts, result.Error
229}
Milo Lina72e2002022-04-06 20:31:13 -0700230
Sabina Leaver759090b2023-01-14 20:42:56 -0800231func (database *Database) ReturnActions() ([]Action, error) {
232 var actions []Action
233 result := database.Find(&actions)
234 return actions, result.Error
235}
236
Emily Markova6b551e02023-02-18 17:37:40 -0800237func (database *Database) ReturnStats2023() ([]Stats2023, error) {
238 var stats2023 []Stats2023
239 result := database.Find(&stats2023)
240 return stats2023, result.Error
241}
242
Philipp Schrader78dc96b2023-03-11 15:23:44 -0800243func (database *Database) ReturnStats2023ForTeam(teamNumber string, matchNumber int32, setNumber int32, compLevel string) ([]Stats2023, error) {
244 var stats2023 []Stats2023
245 result := database.
246 Where("team_number = ? AND match_number = ? AND set_number = ? AND comp_level = ?",
247 teamNumber, matchNumber, setNumber, compLevel).
248 Find(&stats2023)
249 return stats2023, result.Error
250}
251
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700252func (database *Database) ReturnRankings() ([]Ranking, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700253 var rankins []Ranking
254 result := database.Find(&rankins)
255 return rankins, result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700256}
257
Emily Markovab8551572023-03-22 19:49:39 -0700258func (database *Database) queryMatches(teamNumber_ string) ([]TeamMatch, error) {
Emily Markovabf24c9e2023-02-08 20:31:11 -0800259 var matches []TeamMatch
Philipp Schradereecb8962022-06-01 21:02:42 -0700260 result := database.
Emily Markovabf24c9e2023-02-08 20:31:11 -0800261 Where("team_number = $1", teamNumber_).
Philipp Schradereecb8962022-06-01 21:02:42 -0700262 Find(&matches)
263 return matches, result.Error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800264}
265
Emily Markovabf24c9e2023-02-08 20:31:11 -0800266func (database *Database) QueryMatchesString(teamNumber_ string) ([]TeamMatch, error) {
267 var matches []TeamMatch
Sabina Leaver759090b2023-01-14 20:42:56 -0800268 result := database.
Emily Markovabf24c9e2023-02-08 20:31:11 -0800269 Where("team_number = $1", teamNumber_).
Sabina Leaver759090b2023-01-14 20:42:56 -0800270 Find(&matches)
271 return matches, result.Error
272}
273
Milo Lina72e2002022-04-06 20:31:13 -0700274func (database *Database) QueryAllShifts(matchNumber_ int) ([]Shift, error) {
Milo Lina72e2002022-04-06 20:31:13 -0700275 var shifts []Shift
Philipp Schradereecb8962022-06-01 21:02:42 -0700276 result := database.Where("match_number = ?", matchNumber_).Find(&shifts)
277 return shifts, result.Error
Milo Lina72e2002022-04-06 20:31:13 -0700278}
279
Sabina Leaver759090b2023-01-14 20:42:56 -0800280func (database *Database) QueryActions(teamNumber_ int) ([]Action, error) {
281 var actions []Action
282 result := database.
283 Where("team_number = ?", teamNumber_).Find(&actions)
284 return actions, result.Error
285}
286
Philipp Schradereecb8962022-06-01 21:02:42 -0700287func (database *Database) QueryNotes(TeamNumber int32) ([]string, error) {
288 var rawNotes []NotesData
289 result := database.Where("team_number = ?", TeamNumber).Find(&rawNotes)
290 if result.Error != nil {
291 return nil, result.Error
Alex Perry871eab92022-03-12 17:43:52 -0800292 }
Alex Perry871eab92022-03-12 17:43:52 -0800293
Philipp Schradereecb8962022-06-01 21:02:42 -0700294 notes := make([]string, len(rawNotes))
295 for i := range rawNotes {
296 notes[i] = rawNotes[i].Notes
Alex Perry871eab92022-03-12 17:43:52 -0800297 }
Philipp Schradereecb8962022-06-01 21:02:42 -0700298 return notes, nil
Alex Perry871eab92022-03-12 17:43:52 -0800299}
300
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700301func (database *Database) QueryRankings(TeamNumber int) ([]Ranking, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700302 var rankins []Ranking
303 result := database.Where("team_number = ?", TeamNumber).Find(&rankins)
304 return rankins, result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700305}
306
Filip Kujawaf947cb42022-11-21 10:00:30 -0800307func (database *Database) AddNotes(data NotesData) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700308 result := database.Create(&NotesData{
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800309 TeamNumber: data.TeamNumber,
310 Notes: data.Notes,
311 GoodDriving: data.GoodDriving,
312 BadDriving: data.BadDriving,
Filip Kujawa11dc4c92023-04-13 08:55:43 -0700313 SolidPlacing: data.SolidPlacing,
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800314 SketchyPlacing: data.SketchyPlacing,
315 GoodDefense: data.GoodDefense,
316 BadDefense: data.BadDefense,
317 EasilyDefended: data.EasilyDefended,
Philipp Schradereecb8962022-06-01 21:02:42 -0700318 })
319 return result.Error
Alex Perry871eab92022-03-12 17:43:52 -0800320}
Filip Kujawa210a03b2022-11-24 14:41:11 -0800321
322func (database *Database) AddDriverRanking(data DriverRankingData) error {
323 result := database.Create(&DriverRankingData{
324 MatchNumber: data.MatchNumber,
325 Rank1: data.Rank1,
326 Rank2: data.Rank2,
327 Rank3: data.Rank3,
328 })
329 return result.Error
330}
331
Philipp Schradera8955fb2023-03-05 15:47:19 -0800332func (database *Database) AddParsedDriverRanking(data ParsedDriverRankingData) error {
333 result := database.Clauses(clause.OnConflict{
334 UpdateAll: true,
335 }).Create(&data)
336 return result.Error
337}
338
Filip Kujawa210a03b2022-11-24 14:41:11 -0800339func (database *Database) QueryDriverRanking(MatchNumber int) ([]DriverRankingData, error) {
340 var data []DriverRankingData
341 result := database.Where("match_number = ?", MatchNumber).Find(&data)
342 return data, result.Error
343}