blob: 718711c8987148dfde05212df158544b9a27ad3f [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
Philipp Schrader8c878a22023-03-20 22:36:38 -070040 AvgCycle int64
Emily Markova63c63f62023-03-29 20:57:35 -070041 DockedAuto, EngagedAuto, BalanceAttemptAuto bool
42 Docked, Engaged, BalanceAttempt bool
43
Sabina Leaver759090b2023-01-14 20:42:56 -080044 // The username of the person who collected these statistics.
45 // "unknown" if submitted without logging in.
46 // Empty if the stats have not yet been collected.
47 CollectedBy string
48}
49
50type Action struct {
51 TeamNumber string `gorm:"primaryKey"`
52 MatchNumber int32 `gorm:"primaryKey"`
53 SetNumber int32 `gorm:"primaryKey"`
54 CompLevel string `gorm:"primaryKey"`
55 CompletedAction []byte
56 // This contains a serialized scouting.webserver.requests.ActionType flatbuffer.
57 TimeStamp int32 `gorm:"primaryKey"`
58 CollectedBy string
59}
60
Alex Perry871eab92022-03-12 17:43:52 -080061type NotesData struct {
Filip Kujawa7ddd5652023-03-07 19:56:15 -080062 ID uint `gorm:"primaryKey"`
63 TeamNumber int32
64 Notes string
65 GoodDriving bool
66 BadDriving bool
Filip Kujawa6f7f0b32023-03-30 13:26:08 -070067 SolidPickup bool
Filip Kujawa7ddd5652023-03-07 19:56:15 -080068 SketchyPlacing bool
69 GoodDefense bool
70 BadDefense bool
71 EasilyDefended bool
Alex Perry871eab92022-03-12 17:43:52 -080072}
73
Yash Chainanibcd1bb32022-04-02 17:10:24 -070074type Ranking struct {
Philipp Schradereecb8962022-06-01 21:02:42 -070075 TeamNumber int `gorm:"primaryKey"`
Yash Chainanibcd1bb32022-04-02 17:10:24 -070076 Losses, Wins, Ties int32
77 Rank, Dq int32
78}
79
Filip Kujawa210a03b2022-11-24 14:41:11 -080080type DriverRankingData struct {
81 // Each entry in the table is a single scout's ranking.
82 // Multiple scouts can submit a driver ranking for the same
83 // teams in the same match.
84 // The teams being ranked are stored in Rank1, Rank2, Rank3,
85 // Rank1 being the best driving and Rank3 being the worst driving.
86
87 ID uint `gorm:"primaryKey"`
88 MatchNumber int32
89 Rank1 int32
90 Rank2 int32
91 Rank3 int32
92}
93
Philipp Schradera8955fb2023-03-05 15:47:19 -080094type ParsedDriverRankingData struct {
95 // This data stores the output of DriverRank.jl.
96
97 TeamNumber string `gorm:"primaryKey"`
98
99 // The score of the team. A difference of 100 in two team's scores
100 // indicates that one team will outperform the other in 90% of the
101 // matches.
102 Score float32
103}
104
Philipp Schrader7365d322022-03-06 16:40:08 -0800105// Opens a database at the specified port on localhost. We currently don't
106// support connecting to databases on other hosts.
107func NewDatabase(user string, password string, port int) (*Database, error) {
Philipp Schrader83fc2722022-03-10 21:59:20 -0800108 var err error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800109 database := new(Database)
Philipp Schrader83fc2722022-03-10 21:59:20 -0800110
Philipp Schradereecb8962022-06-01 21:02:42 -0700111 dsn := fmt.Sprintf("host=localhost user=%s password=%s dbname=postgres port=%d sslmode=disable", user, password, port)
112 database.DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
113 Logger: logger.Default.LogMode(logger.Silent),
114 })
Philipp Schrader7365d322022-03-06 16:40:08 -0800115 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -0700116 database.Delete()
Philipp Schrader7365d322022-03-06 16:40:08 -0800117 return nil, errors.New(fmt.Sprint("Failed to connect to postgres: ", err))
118 }
Philipp Schrader36df73a2022-03-17 23:27:24 -0700119
Emily Markova132e5be2023-03-25 13:43:05 -0700120 err = database.AutoMigrate(&TeamMatch{}, &Shift{}, &Stats2023{}, &Action{}, &NotesData{}, &Ranking{}, &DriverRankingData{}, &ParsedDriverRankingData{})
Philipp Schrader83fc2722022-03-10 21:59:20 -0800121 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -0700122 database.Delete()
123 return nil, errors.New(fmt.Sprint("Failed to create/migrate tables: ", err))
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700124 }
125
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800126 return database, nil
127}
128
129func (database *Database) Delete() error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700130 sql, err := database.DB.DB()
Philipp Schrader83fc2722022-03-10 21:59:20 -0800131 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -0700132 return err
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800133 }
Philipp Schradereecb8962022-06-01 21:02:42 -0700134 return sql.Close()
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800135}
136
Philipp Schradereecb8962022-06-01 21:02:42 -0700137func (database *Database) SetDebugLogLevel() {
138 database.DB.Logger = database.DB.Logger.LogMode(logger.Info)
139}
Philipp Schradercd12c952022-04-08 18:58:49 -0700140
Emily Markovabf24c9e2023-02-08 20:31:11 -0800141func (database *Database) AddToMatch(m TeamMatch) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700142 result := database.Clauses(clause.OnConflict{
143 UpdateAll: true,
144 }).Create(&m)
145 return result.Error
Philipp Schradercd12c952022-04-08 18:58:49 -0700146}
147
Milo Lina72e2002022-04-06 20:31:13 -0700148func (database *Database) AddToShift(sh Shift) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700149 result := database.Clauses(clause.OnConflict{
150 UpdateAll: true,
151 }).Create(&sh)
152 return result.Error
Milo Lina72e2002022-04-06 20:31:13 -0700153}
154
Sabina Leaver759090b2023-01-14 20:42:56 -0800155func (database *Database) AddAction(a Action) error {
156 result := database.Clauses(clause.OnConflict{
157 UpdateAll: true,
158 }).Create(&a)
159 return result.Error
160}
161
Sabina Leaver759090b2023-01-14 20:42:56 -0800162func (database *Database) AddToStats2023(s Stats2023) error {
163 matches, err := database.QueryMatchesString(s.TeamNumber)
164 if err != nil {
165 return err
166 }
167 foundMatch := false
168 for _, match := range matches {
169 if match.MatchNumber == s.MatchNumber {
170 foundMatch = true
171 break
172 }
173 }
174 if !foundMatch {
175 return errors.New(fmt.Sprint(
176 "Failed to find team ", s.TeamNumber,
177 " in match ", s.MatchNumber, " in the schedule."))
178 }
179
180 result := database.Create(&s)
181 return result.Error
182}
183
Emily Markova6b551e02023-02-18 17:37:40 -0800184func (database *Database) DeleteFromStats(compLevel_ string, matchNumber_ int32, setNumber_ int32, teamNumber_ string) error {
185 var stats2023 []Stats2023
186 result := database.
187 Where("comp_level = ? AND match_number = ? AND set_number = ? AND team_number = ?", compLevel_, matchNumber_, setNumber_, teamNumber_).
188 Delete(&stats2023)
189 return result.Error
190}
191
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700192func (database *Database) AddOrUpdateRankings(r Ranking) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700193 result := database.Clauses(clause.OnConflict{
194 UpdateAll: true,
195 }).Create(&r)
196 return result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700197}
198
Emily Markovabf24c9e2023-02-08 20:31:11 -0800199func (database *Database) ReturnMatches() ([]TeamMatch, error) {
200 var matches []TeamMatch
Philipp Schradereecb8962022-06-01 21:02:42 -0700201 result := database.Find(&matches)
202 return matches, result.Error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800203}
204
Filip Kujawaf882e022022-12-14 13:14:08 -0800205func (database *Database) ReturnAllNotes() ([]NotesData, error) {
206 var notes []NotesData
207 result := database.Find(&notes)
208 return notes, result.Error
209}
210
211func (database *Database) ReturnAllDriverRankings() ([]DriverRankingData, error) {
212 var rankings []DriverRankingData
213 result := database.Find(&rankings)
214 return rankings, result.Error
215}
216
Philipp Schradera8955fb2023-03-05 15:47:19 -0800217func (database *Database) ReturnAllParsedDriverRankings() ([]ParsedDriverRankingData, error) {
218 var rankings []ParsedDriverRankingData
219 result := database.Find(&rankings)
220 return rankings, result.Error
221}
222
Milo Lina72e2002022-04-06 20:31:13 -0700223func (database *Database) ReturnAllShifts() ([]Shift, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700224 var shifts []Shift
225 result := database.Find(&shifts)
226 return shifts, result.Error
227}
Milo Lina72e2002022-04-06 20:31:13 -0700228
Sabina Leaver759090b2023-01-14 20:42:56 -0800229func (database *Database) ReturnActions() ([]Action, error) {
230 var actions []Action
231 result := database.Find(&actions)
232 return actions, result.Error
233}
234
Emily Markova6b551e02023-02-18 17:37:40 -0800235func (database *Database) ReturnStats2023() ([]Stats2023, error) {
236 var stats2023 []Stats2023
237 result := database.Find(&stats2023)
238 return stats2023, result.Error
239}
240
Philipp Schrader78dc96b2023-03-11 15:23:44 -0800241func (database *Database) ReturnStats2023ForTeam(teamNumber string, matchNumber int32, setNumber int32, compLevel string) ([]Stats2023, error) {
242 var stats2023 []Stats2023
243 result := database.
244 Where("team_number = ? AND match_number = ? AND set_number = ? AND comp_level = ?",
245 teamNumber, matchNumber, setNumber, compLevel).
246 Find(&stats2023)
247 return stats2023, result.Error
248}
249
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700250func (database *Database) ReturnRankings() ([]Ranking, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700251 var rankins []Ranking
252 result := database.Find(&rankins)
253 return rankins, result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700254}
255
Emily Markovab8551572023-03-22 19:49:39 -0700256func (database *Database) queryMatches(teamNumber_ string) ([]TeamMatch, error) {
Emily Markovabf24c9e2023-02-08 20:31:11 -0800257 var matches []TeamMatch
Philipp Schradereecb8962022-06-01 21:02:42 -0700258 result := database.
Emily Markovabf24c9e2023-02-08 20:31:11 -0800259 Where("team_number = $1", teamNumber_).
Philipp Schradereecb8962022-06-01 21:02:42 -0700260 Find(&matches)
261 return matches, result.Error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800262}
263
Emily Markovabf24c9e2023-02-08 20:31:11 -0800264func (database *Database) QueryMatchesString(teamNumber_ string) ([]TeamMatch, error) {
265 var matches []TeamMatch
Sabina Leaver759090b2023-01-14 20:42:56 -0800266 result := database.
Emily Markovabf24c9e2023-02-08 20:31:11 -0800267 Where("team_number = $1", teamNumber_).
Sabina Leaver759090b2023-01-14 20:42:56 -0800268 Find(&matches)
269 return matches, result.Error
270}
271
Milo Lina72e2002022-04-06 20:31:13 -0700272func (database *Database) QueryAllShifts(matchNumber_ int) ([]Shift, error) {
Milo Lina72e2002022-04-06 20:31:13 -0700273 var shifts []Shift
Philipp Schradereecb8962022-06-01 21:02:42 -0700274 result := database.Where("match_number = ?", matchNumber_).Find(&shifts)
275 return shifts, result.Error
Milo Lina72e2002022-04-06 20:31:13 -0700276}
277
Sabina Leaver759090b2023-01-14 20:42:56 -0800278func (database *Database) QueryActions(teamNumber_ int) ([]Action, error) {
279 var actions []Action
280 result := database.
281 Where("team_number = ?", teamNumber_).Find(&actions)
282 return actions, result.Error
283}
284
Philipp Schradereecb8962022-06-01 21:02:42 -0700285func (database *Database) QueryNotes(TeamNumber int32) ([]string, error) {
286 var rawNotes []NotesData
287 result := database.Where("team_number = ?", TeamNumber).Find(&rawNotes)
288 if result.Error != nil {
289 return nil, result.Error
Alex Perry871eab92022-03-12 17:43:52 -0800290 }
Alex Perry871eab92022-03-12 17:43:52 -0800291
Philipp Schradereecb8962022-06-01 21:02:42 -0700292 notes := make([]string, len(rawNotes))
293 for i := range rawNotes {
294 notes[i] = rawNotes[i].Notes
Alex Perry871eab92022-03-12 17:43:52 -0800295 }
Philipp Schradereecb8962022-06-01 21:02:42 -0700296 return notes, nil
Alex Perry871eab92022-03-12 17:43:52 -0800297}
298
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700299func (database *Database) QueryRankings(TeamNumber int) ([]Ranking, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700300 var rankins []Ranking
301 result := database.Where("team_number = ?", TeamNumber).Find(&rankins)
302 return rankins, result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700303}
304
Filip Kujawaf947cb42022-11-21 10:00:30 -0800305func (database *Database) AddNotes(data NotesData) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700306 result := database.Create(&NotesData{
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800307 TeamNumber: data.TeamNumber,
308 Notes: data.Notes,
309 GoodDriving: data.GoodDriving,
310 BadDriving: data.BadDriving,
Filip Kujawa6f7f0b32023-03-30 13:26:08 -0700311 SolidPickup: data.SolidPickup,
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800312 SketchyPlacing: data.SketchyPlacing,
313 GoodDefense: data.GoodDefense,
314 BadDefense: data.BadDefense,
315 EasilyDefended: data.EasilyDefended,
Philipp Schradereecb8962022-06-01 21:02:42 -0700316 })
317 return result.Error
Alex Perry871eab92022-03-12 17:43:52 -0800318}
Filip Kujawa210a03b2022-11-24 14:41:11 -0800319
320func (database *Database) AddDriverRanking(data DriverRankingData) error {
321 result := database.Create(&DriverRankingData{
322 MatchNumber: data.MatchNumber,
323 Rank1: data.Rank1,
324 Rank2: data.Rank2,
325 Rank3: data.Rank3,
326 })
327 return result.Error
328}
329
Philipp Schradera8955fb2023-03-05 15:47:19 -0800330func (database *Database) AddParsedDriverRanking(data ParsedDriverRankingData) error {
331 result := database.Clauses(clause.OnConflict{
332 UpdateAll: true,
333 }).Create(&data)
334 return result.Error
335}
336
Filip Kujawa210a03b2022-11-24 14:41:11 -0800337func (database *Database) QueryDriverRanking(MatchNumber int) ([]DriverRankingData, error) {
338 var data []DriverRankingData
339 result := database.Where("match_number = ?", MatchNumber).Find(&data)
340 return data, result.Error
341}