blob: 98d8d7119e7729b702dfbbc3f85cfcb1743da8ec [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 Markova46a69bf2023-03-22 20:45:52 -070041 DockedAuto, EngagedAuto bool
42 Docked, Engaged bool
Sabina Leaver759090b2023-01-14 20:42:56 -080043 // The username of the person who collected these statistics.
44 // "unknown" if submitted without logging in.
45 // Empty if the stats have not yet been collected.
46 CollectedBy string
47}
48
49type Action struct {
50 TeamNumber string `gorm:"primaryKey"`
51 MatchNumber int32 `gorm:"primaryKey"`
52 SetNumber int32 `gorm:"primaryKey"`
53 CompLevel string `gorm:"primaryKey"`
54 CompletedAction []byte
55 // This contains a serialized scouting.webserver.requests.ActionType flatbuffer.
56 TimeStamp int32 `gorm:"primaryKey"`
57 CollectedBy string
58}
59
Alex Perry871eab92022-03-12 17:43:52 -080060type NotesData struct {
Filip Kujawa7ddd5652023-03-07 19:56:15 -080061 ID uint `gorm:"primaryKey"`
62 TeamNumber int32
63 Notes string
64 GoodDriving bool
65 BadDriving bool
66 SketchyPickup bool
67 SketchyPlacing bool
68 GoodDefense bool
69 BadDefense bool
70 EasilyDefended bool
Alex Perry871eab92022-03-12 17:43:52 -080071}
72
Yash Chainanibcd1bb32022-04-02 17:10:24 -070073type Ranking struct {
Philipp Schradereecb8962022-06-01 21:02:42 -070074 TeamNumber int `gorm:"primaryKey"`
Yash Chainanibcd1bb32022-04-02 17:10:24 -070075 Losses, Wins, Ties int32
76 Rank, Dq int32
77}
78
Filip Kujawa210a03b2022-11-24 14:41:11 -080079type DriverRankingData struct {
80 // Each entry in the table is a single scout's ranking.
81 // Multiple scouts can submit a driver ranking for the same
82 // teams in the same match.
83 // The teams being ranked are stored in Rank1, Rank2, Rank3,
84 // Rank1 being the best driving and Rank3 being the worst driving.
85
86 ID uint `gorm:"primaryKey"`
87 MatchNumber int32
88 Rank1 int32
89 Rank2 int32
90 Rank3 int32
91}
92
Philipp Schradera8955fb2023-03-05 15:47:19 -080093type ParsedDriverRankingData struct {
94 // This data stores the output of DriverRank.jl.
95
96 TeamNumber string `gorm:"primaryKey"`
97
98 // The score of the team. A difference of 100 in two team's scores
99 // indicates that one team will outperform the other in 90% of the
100 // matches.
101 Score float32
102}
103
Philipp Schrader7365d322022-03-06 16:40:08 -0800104// Opens a database at the specified port on localhost. We currently don't
105// support connecting to databases on other hosts.
106func NewDatabase(user string, password string, port int) (*Database, error) {
Philipp Schrader83fc2722022-03-10 21:59:20 -0800107 var err error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800108 database := new(Database)
Philipp Schrader83fc2722022-03-10 21:59:20 -0800109
Philipp Schradereecb8962022-06-01 21:02:42 -0700110 dsn := fmt.Sprintf("host=localhost user=%s password=%s dbname=postgres port=%d sslmode=disable", user, password, port)
111 database.DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
112 Logger: logger.Default.LogMode(logger.Silent),
113 })
Philipp Schrader7365d322022-03-06 16:40:08 -0800114 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -0700115 database.Delete()
Philipp Schrader7365d322022-03-06 16:40:08 -0800116 return nil, errors.New(fmt.Sprint("Failed to connect to postgres: ", err))
117 }
Philipp Schrader36df73a2022-03-17 23:27:24 -0700118
Emily Markova132e5be2023-03-25 13:43:05 -0700119 err = database.AutoMigrate(&TeamMatch{}, &Shift{}, &Stats2023{}, &Action{}, &NotesData{}, &Ranking{}, &DriverRankingData{}, &ParsedDriverRankingData{})
Philipp Schrader83fc2722022-03-10 21:59:20 -0800120 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -0700121 database.Delete()
122 return nil, errors.New(fmt.Sprint("Failed to create/migrate tables: ", err))
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700123 }
124
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800125 return database, nil
126}
127
128func (database *Database) Delete() error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700129 sql, err := database.DB.DB()
Philipp Schrader83fc2722022-03-10 21:59:20 -0800130 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -0700131 return err
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800132 }
Philipp Schradereecb8962022-06-01 21:02:42 -0700133 return sql.Close()
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800134}
135
Philipp Schradereecb8962022-06-01 21:02:42 -0700136func (database *Database) SetDebugLogLevel() {
137 database.DB.Logger = database.DB.Logger.LogMode(logger.Info)
138}
Philipp Schradercd12c952022-04-08 18:58:49 -0700139
Emily Markovabf24c9e2023-02-08 20:31:11 -0800140func (database *Database) AddToMatch(m TeamMatch) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700141 result := database.Clauses(clause.OnConflict{
142 UpdateAll: true,
143 }).Create(&m)
144 return result.Error
Philipp Schradercd12c952022-04-08 18:58:49 -0700145}
146
Milo Lina72e2002022-04-06 20:31:13 -0700147func (database *Database) AddToShift(sh Shift) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700148 result := database.Clauses(clause.OnConflict{
149 UpdateAll: true,
150 }).Create(&sh)
151 return result.Error
Milo Lina72e2002022-04-06 20:31:13 -0700152}
153
Sabina Leaver759090b2023-01-14 20:42:56 -0800154func (database *Database) AddAction(a Action) error {
155 result := database.Clauses(clause.OnConflict{
156 UpdateAll: true,
157 }).Create(&a)
158 return result.Error
159}
160
Sabina Leaver759090b2023-01-14 20:42:56 -0800161func (database *Database) AddToStats2023(s Stats2023) error {
162 matches, err := database.QueryMatchesString(s.TeamNumber)
163 if err != nil {
164 return err
165 }
166 foundMatch := false
167 for _, match := range matches {
168 if match.MatchNumber == s.MatchNumber {
169 foundMatch = true
170 break
171 }
172 }
173 if !foundMatch {
174 return errors.New(fmt.Sprint(
175 "Failed to find team ", s.TeamNumber,
176 " in match ", s.MatchNumber, " in the schedule."))
177 }
178
179 result := database.Create(&s)
180 return result.Error
181}
182
Emily Markova6b551e02023-02-18 17:37:40 -0800183func (database *Database) DeleteFromStats(compLevel_ string, matchNumber_ int32, setNumber_ int32, teamNumber_ string) error {
184 var stats2023 []Stats2023
185 result := database.
186 Where("comp_level = ? AND match_number = ? AND set_number = ? AND team_number = ?", compLevel_, matchNumber_, setNumber_, teamNumber_).
187 Delete(&stats2023)
188 return result.Error
189}
190
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700191func (database *Database) AddOrUpdateRankings(r Ranking) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700192 result := database.Clauses(clause.OnConflict{
193 UpdateAll: true,
194 }).Create(&r)
195 return result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700196}
197
Emily Markovabf24c9e2023-02-08 20:31:11 -0800198func (database *Database) ReturnMatches() ([]TeamMatch, error) {
199 var matches []TeamMatch
Philipp Schradereecb8962022-06-01 21:02:42 -0700200 result := database.Find(&matches)
201 return matches, result.Error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800202}
203
Filip Kujawaf882e022022-12-14 13:14:08 -0800204func (database *Database) ReturnAllNotes() ([]NotesData, error) {
205 var notes []NotesData
206 result := database.Find(&notes)
207 return notes, result.Error
208}
209
210func (database *Database) ReturnAllDriverRankings() ([]DriverRankingData, error) {
211 var rankings []DriverRankingData
212 result := database.Find(&rankings)
213 return rankings, result.Error
214}
215
Philipp Schradera8955fb2023-03-05 15:47:19 -0800216func (database *Database) ReturnAllParsedDriverRankings() ([]ParsedDriverRankingData, error) {
217 var rankings []ParsedDriverRankingData
218 result := database.Find(&rankings)
219 return rankings, result.Error
220}
221
Milo Lina72e2002022-04-06 20:31:13 -0700222func (database *Database) ReturnAllShifts() ([]Shift, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700223 var shifts []Shift
224 result := database.Find(&shifts)
225 return shifts, result.Error
226}
Milo Lina72e2002022-04-06 20:31:13 -0700227
Sabina Leaver759090b2023-01-14 20:42:56 -0800228func (database *Database) ReturnActions() ([]Action, error) {
229 var actions []Action
230 result := database.Find(&actions)
231 return actions, result.Error
232}
233
Emily Markova6b551e02023-02-18 17:37:40 -0800234func (database *Database) ReturnStats2023() ([]Stats2023, error) {
235 var stats2023 []Stats2023
236 result := database.Find(&stats2023)
237 return stats2023, result.Error
238}
239
Philipp Schrader78dc96b2023-03-11 15:23:44 -0800240func (database *Database) ReturnStats2023ForTeam(teamNumber string, matchNumber int32, setNumber int32, compLevel string) ([]Stats2023, error) {
241 var stats2023 []Stats2023
242 result := database.
243 Where("team_number = ? AND match_number = ? AND set_number = ? AND comp_level = ?",
244 teamNumber, matchNumber, setNumber, compLevel).
245 Find(&stats2023)
246 return stats2023, result.Error
247}
248
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700249func (database *Database) ReturnRankings() ([]Ranking, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700250 var rankins []Ranking
251 result := database.Find(&rankins)
252 return rankins, result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700253}
254
Emily Markovab8551572023-03-22 19:49:39 -0700255func (database *Database) queryMatches(teamNumber_ string) ([]TeamMatch, error) {
Emily Markovabf24c9e2023-02-08 20:31:11 -0800256 var matches []TeamMatch
Philipp Schradereecb8962022-06-01 21:02:42 -0700257 result := database.
Emily Markovabf24c9e2023-02-08 20:31:11 -0800258 Where("team_number = $1", teamNumber_).
Philipp Schradereecb8962022-06-01 21:02:42 -0700259 Find(&matches)
260 return matches, result.Error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800261}
262
Emily Markovabf24c9e2023-02-08 20:31:11 -0800263func (database *Database) QueryMatchesString(teamNumber_ string) ([]TeamMatch, error) {
264 var matches []TeamMatch
Sabina Leaver759090b2023-01-14 20:42:56 -0800265 result := database.
Emily Markovabf24c9e2023-02-08 20:31:11 -0800266 Where("team_number = $1", teamNumber_).
Sabina Leaver759090b2023-01-14 20:42:56 -0800267 Find(&matches)
268 return matches, result.Error
269}
270
Milo Lina72e2002022-04-06 20:31:13 -0700271func (database *Database) QueryAllShifts(matchNumber_ int) ([]Shift, error) {
Milo Lina72e2002022-04-06 20:31:13 -0700272 var shifts []Shift
Philipp Schradereecb8962022-06-01 21:02:42 -0700273 result := database.Where("match_number = ?", matchNumber_).Find(&shifts)
274 return shifts, result.Error
Milo Lina72e2002022-04-06 20:31:13 -0700275}
276
Sabina Leaver759090b2023-01-14 20:42:56 -0800277func (database *Database) QueryActions(teamNumber_ int) ([]Action, error) {
278 var actions []Action
279 result := database.
280 Where("team_number = ?", teamNumber_).Find(&actions)
281 return actions, result.Error
282}
283
Philipp Schradereecb8962022-06-01 21:02:42 -0700284func (database *Database) QueryNotes(TeamNumber int32) ([]string, error) {
285 var rawNotes []NotesData
286 result := database.Where("team_number = ?", TeamNumber).Find(&rawNotes)
287 if result.Error != nil {
288 return nil, result.Error
Alex Perry871eab92022-03-12 17:43:52 -0800289 }
Alex Perry871eab92022-03-12 17:43:52 -0800290
Philipp Schradereecb8962022-06-01 21:02:42 -0700291 notes := make([]string, len(rawNotes))
292 for i := range rawNotes {
293 notes[i] = rawNotes[i].Notes
Alex Perry871eab92022-03-12 17:43:52 -0800294 }
Philipp Schradereecb8962022-06-01 21:02:42 -0700295 return notes, nil
Alex Perry871eab92022-03-12 17:43:52 -0800296}
297
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700298func (database *Database) QueryRankings(TeamNumber int) ([]Ranking, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700299 var rankins []Ranking
300 result := database.Where("team_number = ?", TeamNumber).Find(&rankins)
301 return rankins, result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700302}
303
Filip Kujawaf947cb42022-11-21 10:00:30 -0800304func (database *Database) AddNotes(data NotesData) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700305 result := database.Create(&NotesData{
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800306 TeamNumber: data.TeamNumber,
307 Notes: data.Notes,
308 GoodDriving: data.GoodDriving,
309 BadDriving: data.BadDriving,
310 SketchyPickup: data.SketchyPickup,
311 SketchyPlacing: data.SketchyPlacing,
312 GoodDefense: data.GoodDefense,
313 BadDefense: data.BadDefense,
314 EasilyDefended: data.EasilyDefended,
Philipp Schradereecb8962022-06-01 21:02:42 -0700315 })
316 return result.Error
Alex Perry871eab92022-03-12 17:43:52 -0800317}
Filip Kujawa210a03b2022-11-24 14:41:11 -0800318
319func (database *Database) AddDriverRanking(data DriverRankingData) error {
320 result := database.Create(&DriverRankingData{
321 MatchNumber: data.MatchNumber,
322 Rank1: data.Rank1,
323 Rank2: data.Rank2,
324 Rank3: data.Rank3,
325 })
326 return result.Error
327}
328
Philipp Schradera8955fb2023-03-05 15:47:19 -0800329func (database *Database) AddParsedDriverRanking(data ParsedDriverRankingData) error {
330 result := database.Clauses(clause.OnConflict{
331 UpdateAll: true,
332 }).Create(&data)
333 return result.Error
334}
335
Filip Kujawa210a03b2022-11-24 14:41:11 -0800336func (database *Database) QueryDriverRanking(MatchNumber int) ([]DriverRankingData, error) {
337 var data []DriverRankingData
338 result := database.Where("match_number = ?", MatchNumber).Find(&data)
339 return data, result.Error
340}