blob: 34f2dac9aa962243dbdcd1f15c9cd78fd51d5915 [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
Emily Markova63c63f62023-03-29 20:57:35 -070042 DockedAuto, EngagedAuto, BalanceAttemptAuto bool
43 Docked, Engaged, BalanceAttempt bool
44
Sabina Leaver759090b2023-01-14 20:42:56 -080045 // The username of the person who collected these statistics.
46 // "unknown" if submitted without logging in.
47 // Empty if the stats have not yet been collected.
48 CollectedBy string
49}
50
51type Action struct {
Sabina Leaver9b4eb312023-02-20 19:58:17 -080052 TeamNumber string `gorm:"primaryKey"`
53 MatchNumber int32 `gorm:"primaryKey"`
54 SetNumber int32 `gorm:"primaryKey"`
55 CompLevel string `gorm:"primaryKey"`
Sabina Leaver759090b2023-01-14 20:42:56 -080056 // This contains a serialized scouting.webserver.requests.ActionType flatbuffer.
Sabina Leaver9b4eb312023-02-20 19:58:17 -080057 CompletedAction []byte
58 Timestamp int64 `gorm:"primaryKey"`
59 CollectedBy string
Sabina Leaver759090b2023-01-14 20:42:56 -080060}
61
Alex Perry871eab92022-03-12 17:43:52 -080062type NotesData struct {
Filip Kujawa7ddd5652023-03-07 19:56:15 -080063 ID uint `gorm:"primaryKey"`
64 TeamNumber int32
65 Notes string
66 GoodDriving bool
67 BadDriving bool
Filip Kujawa11dc4c92023-04-13 08:55:43 -070068 SolidPlacing bool
Filip Kujawa7ddd5652023-03-07 19:56:15 -080069 SketchyPlacing bool
70 GoodDefense bool
71 BadDefense bool
72 EasilyDefended bool
Alex Perry871eab92022-03-12 17:43:52 -080073}
74
Yash Chainanibcd1bb32022-04-02 17:10:24 -070075type Ranking struct {
Philipp Schradereecb8962022-06-01 21:02:42 -070076 TeamNumber int `gorm:"primaryKey"`
Yash Chainanibcd1bb32022-04-02 17:10:24 -070077 Losses, Wins, Ties int32
78 Rank, Dq int32
79}
80
Filip Kujawa210a03b2022-11-24 14:41:11 -080081type DriverRankingData struct {
82 // Each entry in the table is a single scout's ranking.
83 // Multiple scouts can submit a driver ranking for the same
84 // teams in the same match.
85 // The teams being ranked are stored in Rank1, Rank2, Rank3,
86 // Rank1 being the best driving and Rank3 being the worst driving.
87
88 ID uint `gorm:"primaryKey"`
89 MatchNumber int32
90 Rank1 int32
91 Rank2 int32
92 Rank3 int32
93}
94
Philipp Schradera8955fb2023-03-05 15:47:19 -080095type ParsedDriverRankingData struct {
96 // This data stores the output of DriverRank.jl.
97
98 TeamNumber string `gorm:"primaryKey"`
99
100 // The score of the team. A difference of 100 in two team's scores
101 // indicates that one team will outperform the other in 90% of the
102 // matches.
103 Score float32
104}
105
Philipp Schrader7365d322022-03-06 16:40:08 -0800106// Opens a database at the specified port on localhost. We currently don't
107// support connecting to databases on other hosts.
108func NewDatabase(user string, password string, port int) (*Database, error) {
Philipp Schrader83fc2722022-03-10 21:59:20 -0800109 var err error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800110 database := new(Database)
Philipp Schrader83fc2722022-03-10 21:59:20 -0800111
Philipp Schradereecb8962022-06-01 21:02:42 -0700112 dsn := fmt.Sprintf("host=localhost user=%s password=%s dbname=postgres port=%d sslmode=disable", user, password, port)
113 database.DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
114 Logger: logger.Default.LogMode(logger.Silent),
115 })
Philipp Schrader7365d322022-03-06 16:40:08 -0800116 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -0700117 database.Delete()
Philipp Schrader7365d322022-03-06 16:40:08 -0800118 return nil, errors.New(fmt.Sprint("Failed to connect to postgres: ", err))
119 }
Philipp Schrader36df73a2022-03-17 23:27:24 -0700120
Emily Markova132e5be2023-03-25 13:43:05 -0700121 err = database.AutoMigrate(&TeamMatch{}, &Shift{}, &Stats2023{}, &Action{}, &NotesData{}, &Ranking{}, &DriverRankingData{}, &ParsedDriverRankingData{})
Philipp Schrader83fc2722022-03-10 21:59:20 -0800122 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -0700123 database.Delete()
124 return nil, errors.New(fmt.Sprint("Failed to create/migrate tables: ", err))
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700125 }
126
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800127 return database, nil
128}
129
130func (database *Database) Delete() error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700131 sql, err := database.DB.DB()
Philipp Schrader83fc2722022-03-10 21:59:20 -0800132 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -0700133 return err
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800134 }
Philipp Schradereecb8962022-06-01 21:02:42 -0700135 return sql.Close()
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800136}
137
Philipp Schradereecb8962022-06-01 21:02:42 -0700138func (database *Database) SetDebugLogLevel() {
139 database.DB.Logger = database.DB.Logger.LogMode(logger.Info)
140}
Philipp Schradercd12c952022-04-08 18:58:49 -0700141
Emily Markovabf24c9e2023-02-08 20:31:11 -0800142func (database *Database) AddToMatch(m TeamMatch) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700143 result := database.Clauses(clause.OnConflict{
144 UpdateAll: true,
145 }).Create(&m)
146 return result.Error
Philipp Schradercd12c952022-04-08 18:58:49 -0700147}
148
Milo Lina72e2002022-04-06 20:31:13 -0700149func (database *Database) AddToShift(sh Shift) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700150 result := database.Clauses(clause.OnConflict{
151 UpdateAll: true,
152 }).Create(&sh)
153 return result.Error
Milo Lina72e2002022-04-06 20:31:13 -0700154}
155
Sabina Leaver759090b2023-01-14 20:42:56 -0800156func (database *Database) AddAction(a Action) error {
157 result := database.Clauses(clause.OnConflict{
158 UpdateAll: true,
159 }).Create(&a)
160 return result.Error
161}
162
Sabina Leaver759090b2023-01-14 20:42:56 -0800163func (database *Database) AddToStats2023(s Stats2023) error {
164 matches, err := database.QueryMatchesString(s.TeamNumber)
165 if err != nil {
166 return err
167 }
168 foundMatch := false
169 for _, match := range matches {
170 if match.MatchNumber == s.MatchNumber {
171 foundMatch = true
172 break
173 }
174 }
175 if !foundMatch {
176 return errors.New(fmt.Sprint(
177 "Failed to find team ", s.TeamNumber,
178 " in match ", s.MatchNumber, " in the schedule."))
179 }
180
181 result := database.Create(&s)
182 return result.Error
183}
184
Emily Markova6b551e02023-02-18 17:37:40 -0800185func (database *Database) DeleteFromStats(compLevel_ string, matchNumber_ int32, setNumber_ int32, teamNumber_ string) error {
186 var stats2023 []Stats2023
187 result := database.
188 Where("comp_level = ? AND match_number = ? AND set_number = ? AND team_number = ?", compLevel_, matchNumber_, setNumber_, teamNumber_).
189 Delete(&stats2023)
190 return result.Error
191}
192
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700193func (database *Database) AddOrUpdateRankings(r Ranking) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700194 result := database.Clauses(clause.OnConflict{
195 UpdateAll: true,
196 }).Create(&r)
197 return result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700198}
199
Emily Markovabf24c9e2023-02-08 20:31:11 -0800200func (database *Database) ReturnMatches() ([]TeamMatch, error) {
201 var matches []TeamMatch
Philipp Schradereecb8962022-06-01 21:02:42 -0700202 result := database.Find(&matches)
203 return matches, result.Error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800204}
205
Filip Kujawaf882e022022-12-14 13:14:08 -0800206func (database *Database) ReturnAllNotes() ([]NotesData, error) {
207 var notes []NotesData
208 result := database.Find(&notes)
209 return notes, result.Error
210}
211
212func (database *Database) ReturnAllDriverRankings() ([]DriverRankingData, error) {
213 var rankings []DriverRankingData
214 result := database.Find(&rankings)
215 return rankings, result.Error
216}
217
Philipp Schradera8955fb2023-03-05 15:47:19 -0800218func (database *Database) ReturnAllParsedDriverRankings() ([]ParsedDriverRankingData, error) {
219 var rankings []ParsedDriverRankingData
220 result := database.Find(&rankings)
221 return rankings, result.Error
222}
223
Milo Lina72e2002022-04-06 20:31:13 -0700224func (database *Database) ReturnAllShifts() ([]Shift, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700225 var shifts []Shift
226 result := database.Find(&shifts)
227 return shifts, result.Error
228}
Milo Lina72e2002022-04-06 20:31:13 -0700229
Sabina Leaver759090b2023-01-14 20:42:56 -0800230func (database *Database) ReturnActions() ([]Action, error) {
231 var actions []Action
232 result := database.Find(&actions)
233 return actions, result.Error
234}
235
Emily Markova6b551e02023-02-18 17:37:40 -0800236func (database *Database) ReturnStats2023() ([]Stats2023, error) {
237 var stats2023 []Stats2023
238 result := database.Find(&stats2023)
239 return stats2023, result.Error
240}
241
Philipp Schrader78dc96b2023-03-11 15:23:44 -0800242func (database *Database) ReturnStats2023ForTeam(teamNumber string, matchNumber int32, setNumber int32, compLevel string) ([]Stats2023, error) {
243 var stats2023 []Stats2023
244 result := database.
245 Where("team_number = ? AND match_number = ? AND set_number = ? AND comp_level = ?",
246 teamNumber, matchNumber, setNumber, compLevel).
247 Find(&stats2023)
248 return stats2023, result.Error
249}
250
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700251func (database *Database) ReturnRankings() ([]Ranking, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700252 var rankins []Ranking
253 result := database.Find(&rankins)
254 return rankins, result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700255}
256
Emily Markovab8551572023-03-22 19:49:39 -0700257func (database *Database) queryMatches(teamNumber_ string) ([]TeamMatch, error) {
Emily Markovabf24c9e2023-02-08 20:31:11 -0800258 var matches []TeamMatch
Philipp Schradereecb8962022-06-01 21:02:42 -0700259 result := database.
Emily Markovabf24c9e2023-02-08 20:31:11 -0800260 Where("team_number = $1", teamNumber_).
Philipp Schradereecb8962022-06-01 21:02:42 -0700261 Find(&matches)
262 return matches, result.Error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800263}
264
Emily Markovabf24c9e2023-02-08 20:31:11 -0800265func (database *Database) QueryMatchesString(teamNumber_ string) ([]TeamMatch, error) {
266 var matches []TeamMatch
Sabina Leaver759090b2023-01-14 20:42:56 -0800267 result := database.
Emily Markovabf24c9e2023-02-08 20:31:11 -0800268 Where("team_number = $1", teamNumber_).
Sabina Leaver759090b2023-01-14 20:42:56 -0800269 Find(&matches)
270 return matches, result.Error
271}
272
Milo Lina72e2002022-04-06 20:31:13 -0700273func (database *Database) QueryAllShifts(matchNumber_ int) ([]Shift, error) {
Milo Lina72e2002022-04-06 20:31:13 -0700274 var shifts []Shift
Philipp Schradereecb8962022-06-01 21:02:42 -0700275 result := database.Where("match_number = ?", matchNumber_).Find(&shifts)
276 return shifts, result.Error
Milo Lina72e2002022-04-06 20:31:13 -0700277}
278
Sabina Leaver759090b2023-01-14 20:42:56 -0800279func (database *Database) QueryActions(teamNumber_ int) ([]Action, error) {
280 var actions []Action
281 result := database.
282 Where("team_number = ?", teamNumber_).Find(&actions)
283 return actions, result.Error
284}
285
Philipp Schradereecb8962022-06-01 21:02:42 -0700286func (database *Database) QueryNotes(TeamNumber int32) ([]string, error) {
287 var rawNotes []NotesData
288 result := database.Where("team_number = ?", TeamNumber).Find(&rawNotes)
289 if result.Error != nil {
290 return nil, result.Error
Alex Perry871eab92022-03-12 17:43:52 -0800291 }
Alex Perry871eab92022-03-12 17:43:52 -0800292
Philipp Schradereecb8962022-06-01 21:02:42 -0700293 notes := make([]string, len(rawNotes))
294 for i := range rawNotes {
295 notes[i] = rawNotes[i].Notes
Alex Perry871eab92022-03-12 17:43:52 -0800296 }
Philipp Schradereecb8962022-06-01 21:02:42 -0700297 return notes, nil
Alex Perry871eab92022-03-12 17:43:52 -0800298}
299
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700300func (database *Database) QueryRankings(TeamNumber int) ([]Ranking, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700301 var rankins []Ranking
302 result := database.Where("team_number = ?", TeamNumber).Find(&rankins)
303 return rankins, result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700304}
305
Filip Kujawaf947cb42022-11-21 10:00:30 -0800306func (database *Database) AddNotes(data NotesData) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700307 result := database.Create(&NotesData{
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800308 TeamNumber: data.TeamNumber,
309 Notes: data.Notes,
310 GoodDriving: data.GoodDriving,
311 BadDriving: data.BadDriving,
Filip Kujawa11dc4c92023-04-13 08:55:43 -0700312 SolidPlacing: data.SolidPlacing,
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800313 SketchyPlacing: data.SketchyPlacing,
314 GoodDefense: data.GoodDefense,
315 BadDefense: data.BadDefense,
316 EasilyDefended: data.EasilyDefended,
Philipp Schradereecb8962022-06-01 21:02:42 -0700317 })
318 return result.Error
Alex Perry871eab92022-03-12 17:43:52 -0800319}
Filip Kujawa210a03b2022-11-24 14:41:11 -0800320
321func (database *Database) AddDriverRanking(data DriverRankingData) error {
322 result := database.Create(&DriverRankingData{
323 MatchNumber: data.MatchNumber,
324 Rank1: data.Rank1,
325 Rank2: data.Rank2,
326 Rank3: data.Rank3,
327 })
328 return result.Error
329}
330
Philipp Schradera8955fb2023-03-05 15:47:19 -0800331func (database *Database) AddParsedDriverRanking(data ParsedDriverRankingData) error {
332 result := database.Clauses(clause.OnConflict{
333 UpdateAll: true,
334 }).Create(&data)
335 return result.Error
336}
337
Filip Kujawa210a03b2022-11-24 14:41:11 -0800338func (database *Database) QueryDriverRanking(MatchNumber int) ([]DriverRankingData, error) {
339 var data []DriverRankingData
340 result := database.Where("match_number = ?", MatchNumber).Find(&data)
341 return data, result.Error
342}