blob: 82f7fa8797ca61cf6b349aac6ea7d24dbece76e2 [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"
6
Philipp Schradereecb8962022-06-01 21:02:42 -07007 "gorm.io/driver/postgres"
8 "gorm.io/gorm"
9 "gorm.io/gorm/clause"
10 "gorm.io/gorm/logger"
Sabina Leaverc5fd2772022-01-29 17:00:23 -080011)
12
13type Database struct {
Philipp Schradereecb8962022-06-01 21:02:42 -070014 *gorm.DB
Sabina Leaverc5fd2772022-01-29 17:00:23 -080015}
16
17type Match struct {
Philipp Schradereecb8962022-06-01 21:02:42 -070018 // TODO(phil): Rework this be be one team per row.
19 // Makes queries much simpler.
20 MatchNumber int32 `gorm:"primaryKey"`
21 SetNumber int32 `gorm:"primaryKey"`
22 CompLevel string `gorm:"primaryKey"`
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080023 R1, R2, R3, B1, B2, B3 int32
Sabina Leaverc5fd2772022-01-29 17:00:23 -080024}
25
Milo Lina72e2002022-04-06 20:31:13 -070026type Shift struct {
Philipp Schradereecb8962022-06-01 21:02:42 -070027 MatchNumber int32 `gorm:"primaryKey"`
Milo Lina72e2002022-04-06 20:31:13 -070028 R1scouter, R2scouter, R3scouter, B1scouter, B2scouter, B3scouter string
29}
30
Sabina Leaverc5fd2772022-01-29 17:00:23 -080031type Stats struct {
Philipp Schradereecb8962022-06-01 21:02:42 -070032 TeamNumber int32 `gorm:"primaryKey"`
33 MatchNumber int32 `gorm:"primaryKey"`
34 SetNumber int32 `gorm:"primaryKey"`
35 CompLevel string `gorm:"primaryKey"`
36 StartingQuadrant int32
37 // This field is for the balls picked up during auto. Use this field
38 // when using this library. Ignore the AutoBallPickedUpX fields below.
39 AutoBallPickedUp [5]bool `gorm:"-:all"`
40 // These fields are internal implementation details. Do not use these.
41 // TODO(phil): Figure out how to use the JSON gorm serializer instead
42 // of manually serializing/deserializing these.
43 AutoBallPickedUp1 bool
44 AutoBallPickedUp2 bool
45 AutoBallPickedUp3 bool
46 AutoBallPickedUp4 bool
47 AutoBallPickedUp5 bool
Philipp Schraderfee07e12022-03-17 22:19:47 -070048 // TODO(phil): Re-order auto and teleop fields so auto comes first.
Philipp Schraderfa45d742022-03-18 19:29:05 -070049 ShotsMissed, UpperGoalShots, LowerGoalShots int32
50 ShotsMissedAuto, UpperGoalAuto, LowerGoalAuto int32
51 PlayedDefense, DefenseReceivedScore int32
Philipp Schrader36df73a2022-03-17 23:27:24 -070052 // Climbing level:
53 // 0 -> "NoAttempt"
54 // 1 -> "Failed"
55 // 2 -> "FailedWithPlentyOfTime"
56 // 3 -> "Low"
57 // 4 -> "Medium"
58 // 5 -> "High"
Philipp Schrader85f6e5b2022-04-16 14:20:06 -070059 // 6 -> "Traversal"
Philipp Schrader36df73a2022-03-17 23:27:24 -070060 Climbing int32
Philipp Schraderfa45d742022-03-18 19:29:05 -070061 // Some non-numerical data that the scout felt worth noting.
62 Comment string
Philipp Schraderfae8a7e2022-03-13 22:51:54 -070063 // The username of the person who collected these statistics.
64 // "unknown" if submitted without logging in.
65 // Empty if the stats have not yet been collected.
66 CollectedBy string
Sabina Leaverc5fd2772022-01-29 17:00:23 -080067}
68
Alex Perry871eab92022-03-12 17:43:52 -080069type NotesData struct {
Filip Kujawaf947cb42022-11-21 10:00:30 -080070 ID uint `gorm:"primaryKey"`
71 TeamNumber int32
72 Notes string
73 GoodDriving bool
74 BadDriving bool
75 SketchyClimb bool
76 SolidClimb bool
77 GoodDefense bool
78 BadDefense bool
Alex Perry871eab92022-03-12 17:43:52 -080079}
80
Yash Chainanibcd1bb32022-04-02 17:10:24 -070081type Ranking struct {
Philipp Schradereecb8962022-06-01 21:02:42 -070082 TeamNumber int `gorm:"primaryKey"`
Yash Chainanibcd1bb32022-04-02 17:10:24 -070083 Losses, Wins, Ties int32
84 Rank, Dq int32
85}
86
Philipp Schrader7365d322022-03-06 16:40:08 -080087// Opens a database at the specified port on localhost. We currently don't
88// support connecting to databases on other hosts.
89func NewDatabase(user string, password string, port int) (*Database, error) {
Philipp Schrader83fc2722022-03-10 21:59:20 -080090 var err error
Sabina Leaverc5fd2772022-01-29 17:00:23 -080091 database := new(Database)
Philipp Schrader83fc2722022-03-10 21:59:20 -080092
Philipp Schradereecb8962022-06-01 21:02:42 -070093 dsn := fmt.Sprintf("host=localhost user=%s password=%s dbname=postgres port=%d sslmode=disable", user, password, port)
94 database.DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
95 Logger: logger.Default.LogMode(logger.Silent),
96 })
Philipp Schrader7365d322022-03-06 16:40:08 -080097 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -070098 database.Delete()
Philipp Schrader7365d322022-03-06 16:40:08 -080099 return nil, errors.New(fmt.Sprint("Failed to connect to postgres: ", err))
100 }
Philipp Schrader36df73a2022-03-17 23:27:24 -0700101
Philipp Schradereecb8962022-06-01 21:02:42 -0700102 err = database.AutoMigrate(&Match{}, &Shift{}, &Stats{}, &NotesData{}, &Ranking{})
Philipp Schrader83fc2722022-03-10 21:59:20 -0800103 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -0700104 database.Delete()
105 return nil, errors.New(fmt.Sprint("Failed to create/migrate tables: ", err))
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700106 }
107
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800108 return database, nil
109}
110
111func (database *Database) Delete() error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700112 sql, err := database.DB.DB()
Philipp Schrader83fc2722022-03-10 21:59:20 -0800113 if err != nil {
Philipp Schradereecb8962022-06-01 21:02:42 -0700114 return err
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800115 }
Philipp Schradereecb8962022-06-01 21:02:42 -0700116 return sql.Close()
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800117}
118
Philipp Schradereecb8962022-06-01 21:02:42 -0700119func (database *Database) SetDebugLogLevel() {
120 database.DB.Logger = database.DB.Logger.LogMode(logger.Info)
121}
Philipp Schradercd12c952022-04-08 18:58:49 -0700122
Philipp Schradereecb8962022-06-01 21:02:42 -0700123func (database *Database) AddToMatch(m Match) error {
124 result := database.Clauses(clause.OnConflict{
125 UpdateAll: true,
126 }).Create(&m)
127 return result.Error
Philipp Schradercd12c952022-04-08 18:58:49 -0700128}
129
Milo Lina72e2002022-04-06 20:31:13 -0700130func (database *Database) AddToShift(sh Shift) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700131 result := database.Clauses(clause.OnConflict{
132 UpdateAll: true,
133 }).Create(&sh)
134 return result.Error
Milo Lina72e2002022-04-06 20:31:13 -0700135}
136
Philipp Schradercd12c952022-04-08 18:58:49 -0700137func (database *Database) AddToStats(s Stats) error {
138 matches, err := database.QueryMatches(s.TeamNumber)
139 if err != nil {
140 return err
141 }
142 foundMatch := false
143 for _, match := range matches {
144 if match.MatchNumber == s.MatchNumber {
145 foundMatch = true
146 break
147 }
148 }
149 if !foundMatch {
150 return errors.New(fmt.Sprint(
151 "Failed to find team ", s.TeamNumber,
152 " in match ", s.MatchNumber, " in the schedule."))
153 }
154
Philipp Schradereecb8962022-06-01 21:02:42 -0700155 // Unpack the auto balls array.
156 s.AutoBallPickedUp1 = s.AutoBallPickedUp[0]
157 s.AutoBallPickedUp2 = s.AutoBallPickedUp[1]
158 s.AutoBallPickedUp3 = s.AutoBallPickedUp[2]
159 s.AutoBallPickedUp4 = s.AutoBallPickedUp[3]
160 s.AutoBallPickedUp5 = s.AutoBallPickedUp[4]
161 result := database.Create(&s)
162 return result.Error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800163}
164
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700165func (database *Database) AddOrUpdateRankings(r Ranking) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700166 result := database.Clauses(clause.OnConflict{
167 UpdateAll: true,
168 }).Create(&r)
169 return result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700170}
171
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800172func (database *Database) ReturnMatches() ([]Match, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700173 var matches []Match
174 result := database.Find(&matches)
175 return matches, result.Error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800176}
177
Milo Lina72e2002022-04-06 20:31:13 -0700178func (database *Database) ReturnAllShifts() ([]Shift, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700179 var shifts []Shift
180 result := database.Find(&shifts)
181 return shifts, result.Error
182}
Milo Lina72e2002022-04-06 20:31:13 -0700183
Philipp Schradereecb8962022-06-01 21:02:42 -0700184// Packs the stats. This really just consists of taking the individual auto
185// ball booleans and turning them into an array. The individual booleans are
186// cleared so that they don't affect struct comparisons.
187func packStats(stats *Stats) {
188 stats.AutoBallPickedUp = [5]bool{
189 stats.AutoBallPickedUp1,
190 stats.AutoBallPickedUp2,
191 stats.AutoBallPickedUp3,
192 stats.AutoBallPickedUp4,
193 stats.AutoBallPickedUp5,
Milo Lina72e2002022-04-06 20:31:13 -0700194 }
Philipp Schradereecb8962022-06-01 21:02:42 -0700195 stats.AutoBallPickedUp1 = false
196 stats.AutoBallPickedUp2 = false
197 stats.AutoBallPickedUp3 = false
198 stats.AutoBallPickedUp4 = false
199 stats.AutoBallPickedUp5 = false
Milo Lina72e2002022-04-06 20:31:13 -0700200}
201
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800202func (database *Database) ReturnStats() ([]Stats, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700203 var stats []Stats
204 result := database.Find(&stats)
205 // Pack the auto balls array.
206 for i := range stats {
207 packStats(&stats[i])
Philipp Schrader30005e42022-03-06 13:53:58 -0800208 }
Philipp Schradereecb8962022-06-01 21:02:42 -0700209 return stats, result.Error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800210}
211
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700212func (database *Database) ReturnRankings() ([]Ranking, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700213 var rankins []Ranking
214 result := database.Find(&rankins)
215 return rankins, result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700216}
217
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800218func (database *Database) QueryMatches(teamNumber_ int32) ([]Match, error) {
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800219 var matches []Match
Philipp Schradereecb8962022-06-01 21:02:42 -0700220 result := database.
221 Where("r1 = $1 OR r2 = $1 OR r3 = $1 OR b1 = $1 OR b2 = $1 OR b3 = $1", teamNumber_).
222 Find(&matches)
223 return matches, result.Error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800224}
225
Milo Lina72e2002022-04-06 20:31:13 -0700226func (database *Database) QueryAllShifts(matchNumber_ int) ([]Shift, error) {
Milo Lina72e2002022-04-06 20:31:13 -0700227 var shifts []Shift
Philipp Schradereecb8962022-06-01 21:02:42 -0700228 result := database.Where("match_number = ?", matchNumber_).Find(&shifts)
229 return shifts, result.Error
Milo Lina72e2002022-04-06 20:31:13 -0700230}
231
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800232func (database *Database) QueryStats(teamNumber_ int) ([]Stats, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700233 var stats []Stats
234 result := database.Where("team_number = ?", teamNumber_).Find(&stats)
235 // Pack the auto balls array.
236 for i := range stats {
237 packStats(&stats[i])
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800238 }
Philipp Schradereecb8962022-06-01 21:02:42 -0700239 return stats, result.Error
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800240}
Alex Perry871eab92022-03-12 17:43:52 -0800241
Philipp Schradereecb8962022-06-01 21:02:42 -0700242func (database *Database) QueryNotes(TeamNumber int32) ([]string, error) {
243 var rawNotes []NotesData
244 result := database.Where("team_number = ?", TeamNumber).Find(&rawNotes)
245 if result.Error != nil {
246 return nil, result.Error
Alex Perry871eab92022-03-12 17:43:52 -0800247 }
Alex Perry871eab92022-03-12 17:43:52 -0800248
Philipp Schradereecb8962022-06-01 21:02:42 -0700249 notes := make([]string, len(rawNotes))
250 for i := range rawNotes {
251 notes[i] = rawNotes[i].Notes
Alex Perry871eab92022-03-12 17:43:52 -0800252 }
Philipp Schradereecb8962022-06-01 21:02:42 -0700253 return notes, nil
Alex Perry871eab92022-03-12 17:43:52 -0800254}
255
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700256func (database *Database) QueryRankings(TeamNumber int) ([]Ranking, error) {
Philipp Schradereecb8962022-06-01 21:02:42 -0700257 var rankins []Ranking
258 result := database.Where("team_number = ?", TeamNumber).Find(&rankins)
259 return rankins, result.Error
Yash Chainanibcd1bb32022-04-02 17:10:24 -0700260}
261
Filip Kujawaf947cb42022-11-21 10:00:30 -0800262func (database *Database) AddNotes(data NotesData) error {
Philipp Schradereecb8962022-06-01 21:02:42 -0700263 result := database.Create(&NotesData{
Filip Kujawaf947cb42022-11-21 10:00:30 -0800264 TeamNumber: data.TeamNumber,
265 Notes: data.Notes,
266 GoodDriving: data.GoodDriving,
267 BadDriving: data.BadDriving,
268 SketchyClimb: data.SketchyClimb,
269 SolidClimb: data.SolidClimb,
270 GoodDefense: data.GoodDefense,
271 BadDefense: data.BadDefense,
Philipp Schradereecb8962022-06-01 21:02:42 -0700272 })
273 return result.Error
Alex Perry871eab92022-03-12 17:43:52 -0800274}