blob: 94e905605ac719d61e4bdb587ea0c0d05912b9d9 [file] [log] [blame]
Sabina Leaverc5fd2772022-01-29 17:00:23 -08001package db
2
3import (
4 "database/sql"
Philipp Schrader30005e42022-03-06 13:53:58 -08005 "errors"
Sabina Leaverc5fd2772022-01-29 17:00:23 -08006 "fmt"
7
8 _ "github.com/mattn/go-sqlite3"
9)
10
11type Database struct {
12 *sql.DB
13}
14
15type Match struct {
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080016 MatchNumber, Round int32
Philipp Schrader1e6c0a92022-02-27 23:30:57 -080017 CompLevel string
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080018 R1, R2, R3, B1, B2, B3 int32
Sabina Leaverc5fd2772022-01-29 17:00:23 -080019 // Each of these variables holds the matchID of the corresponding Stats row
20 r1ID, r2ID, r3ID, b1ID, b2ID, b3ID int
21}
22
23type Stats struct {
Philipp Schrader80ccb662022-03-01 21:47:30 -080024 TeamNumber, MatchNumber int32
25 ShotsMissed, UpperGoalShots, LowerGoalShots int32
26 ShotsMissedAuto, UpperGoalAuto, LowerGoalAuto, PlayedDefense int32
27 Climbing int32
Sabina Leaverc5fd2772022-01-29 17:00:23 -080028}
29
Alex Perry871eab92022-03-12 17:43:52 -080030type NotesData struct {
31 TeamNumber int32
32 Notes []string
33}
34
Philipp Schrader4953cc32022-02-25 18:09:02 -080035// Opens a database at the specified path. If the path refers to a non-existent
36// file, the database will be created and initialized with empty tables.
37func NewDatabase(path string) (*Database, error) {
Philipp Schrader83fc2722022-03-10 21:59:20 -080038 var err error
Sabina Leaverc5fd2772022-01-29 17:00:23 -080039 database := new(Database)
Philipp Schrader83fc2722022-03-10 21:59:20 -080040 database.DB, err = sql.Open("sqlite3", path)
41 if err != nil {
42 return nil, errors.New(fmt.Sprint("Failed to create postgres db: ", err))
Sabina Leaverc5fd2772022-01-29 17:00:23 -080043 }
Philipp Schrader83fc2722022-03-10 21:59:20 -080044
45 statement, err := database.Prepare("CREATE TABLE IF NOT EXISTS matches (" +
46 "id INTEGER PRIMARY KEY, " +
47 "MatchNumber INTEGER, " +
48 "Round INTEGER, " +
49 "CompLevel INTEGER, " +
50 "R1 INTEGER, " +
51 "R2 INTEGER, " +
52 "R3 INTEGER, " +
53 "B1 INTEGER, " +
54 "B2 INTEGER, " +
55 "B3 INTEGER, " +
56 "r1ID INTEGER, " +
57 "r2ID INTEGER, " +
58 "r3ID INTEGER, " +
59 "b1ID INTEGER, " +
60 "b2ID INTEGER, " +
61 "b3ID INTEGER)")
62 if err != nil {
63 return nil, errors.New(fmt.Sprint("Failed to prepare matches table creation: ", err))
Sabina Leaverc5fd2772022-01-29 17:00:23 -080064 }
Philipp Schrader83fc2722022-03-10 21:59:20 -080065 defer statement.Close()
66
67 _, err = statement.Exec()
68 if err != nil {
69 return nil, errors.New(fmt.Sprint("Failed to create matches table: ", err))
70 }
71
72 statement, err = database.Prepare("CREATE TABLE IF NOT EXISTS team_match_stats (" +
73 "id INTEGER PRIMARY KEY, " +
74 "TeamNumber INTEGER, " +
75 "MatchNumber INTEGER, " +
76 "ShotsMissed INTEGER, " +
77 "UpperGoalShots INTEGER, " +
78 "LowerGoalShots INTEGER, " +
79 "ShotsMissedAuto INTEGER, " +
80 "UpperGoalAuto INTEGER, " +
81 "LowerGoalAuto INTEGER, " +
82 "PlayedDefense INTEGER, " +
83 "Climbing INTEGER)")
84 if err != nil {
85 return nil, errors.New(fmt.Sprint("Failed to prepare stats table creation: ", err))
86 }
87 defer statement.Close()
88
89 _, err = statement.Exec()
90 if err != nil {
91 return nil, errors.New(fmt.Sprint("Failed to create team_match_stats table: ", err))
92 }
93
Alex Perry871eab92022-03-12 17:43:52 -080094 statement, err = database.Prepare("CREATE TABLE IF NOT EXISTS team_notes (" +
95 "id INTEGER PRIMARY KEY, " +
96 "TeamNumber INTEGER, " +
97 "Notes TEXT)")
98 if err != nil {
99 return nil, errors.New(fmt.Sprint("Failed to prepare notes table creation: ", err))
100 }
101 defer statement.Close()
102
103 _, err = statement.Exec()
104 if err != nil {
105 return nil, errors.New(fmt.Sprint("Failed to create notes table: ", err))
106 }
107
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800108 return database, nil
109}
110
111func (database *Database) Delete() error {
Philipp Schrader83fc2722022-03-10 21:59:20 -0800112 statement, err := database.Prepare("DROP TABLE IF EXISTS matches")
113 if err != nil {
114 return errors.New(fmt.Sprint("Failed to prepare dropping matches table: ", err))
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800115 }
Philipp Schrader83fc2722022-03-10 21:59:20 -0800116 _, err = statement.Exec()
117 if err != nil {
118 return errors.New(fmt.Sprint("Failed to drop matches table: ", err))
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800119 }
Philipp Schrader83fc2722022-03-10 21:59:20 -0800120
121 statement, err = database.Prepare("DROP TABLE IF EXISTS team_match_stats")
122 if err != nil {
123 return errors.New(fmt.Sprint("Failed to prepare dropping stats table: ", err))
124 }
125 _, err = statement.Exec()
126 if err != nil {
127 return errors.New(fmt.Sprint("Failed to drop stats table: ", err))
128 }
Alex Perry871eab92022-03-12 17:43:52 -0800129
130 statement, err = database.Prepare("DROP TABLE IF EXISTS team_notes")
131 if err != nil {
132 return errors.New(fmt.Sprint("Failed to prepare dropping notes table: ", err))
133 }
134 _, err = statement.Exec()
135 if err != nil {
136 return errors.New(fmt.Sprint("Failed to drop notes table: ", err))
137 }
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800138 return nil
139}
140
141// This function will also populate the Stats table with six empty rows every time a match is added
142func (database *Database) AddToMatch(m Match) error {
Philipp Schrader83fc2722022-03-10 21:59:20 -0800143 statement, err := database.Prepare("INSERT INTO team_match_stats(" +
144 "TeamNumber, MatchNumber, " +
145 "ShotsMissed, UpperGoalShots, LowerGoalShots, " +
146 "ShotsMissedAuto, UpperGoalAuto, LowerGoalAuto, " +
147 "PlayedDefense, Climbing) " +
148 "VALUES (" +
149 "?, ?, " +
150 "?, ?, ?, " +
151 "?, ?, ?, " +
152 "?, ?)")
153 if err != nil {
154 return errors.New(fmt.Sprint("Failed to prepare insertion into stats database: ", err))
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800155 }
Philipp Schrader83fc2722022-03-10 21:59:20 -0800156 defer statement.Close()
157
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800158 var rowIds [6]int64
Philipp Schrader80ccb662022-03-01 21:47:30 -0800159 for i, TeamNumber := range []int32{m.R1, m.R2, m.R3, m.B1, m.B2, m.B3} {
Philipp Schrader83fc2722022-03-10 21:59:20 -0800160 result, err := statement.Exec(TeamNumber, m.MatchNumber, 0, 0, 0, 0, 0, 0, 0, 0)
161 if err != nil {
162 return errors.New(fmt.Sprint("Failed to insert stats: ", err))
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800163 }
Philipp Schrader83fc2722022-03-10 21:59:20 -0800164 rowIds[i], err = result.LastInsertId()
165 if err != nil {
166 return errors.New(fmt.Sprint("Failed to get last insert ID: ", err))
167 }
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800168 }
Philipp Schrader83fc2722022-03-10 21:59:20 -0800169
170 statement, err = database.Prepare("INSERT INTO matches(" +
171 "MatchNumber, Round, CompLevel, " +
172 "R1, R2, R3, B1, B2, B3, " +
173 "r1ID, r2ID, r3ID, b1ID, b2ID, b3ID) " +
174 "VALUES (" +
175 "?, ?, ?, " +
176 "?, ?, ?, ?, ?, ?, " +
177 "?, ?, ?, ?, ?, ?)")
178 if err != nil {
179 return errors.New(fmt.Sprint("Failed to prepare insertion into match database: ", err))
180 }
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800181 defer statement.Close()
Philipp Schrader83fc2722022-03-10 21:59:20 -0800182
183 _, err = statement.Exec(m.MatchNumber, m.Round, m.CompLevel,
184 m.R1, m.R2, m.R3, m.B1, m.B2, m.B3,
185 rowIds[0], rowIds[1], rowIds[2], rowIds[3], rowIds[4], rowIds[5])
186 if err != nil {
187 return errors.New(fmt.Sprint("Failed to insert into match database: ", err))
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800188 }
189 return nil
190}
191
192func (database *Database) AddToStats(s Stats) error {
Philipp Schrader83fc2722022-03-10 21:59:20 -0800193 statement, err := database.Prepare("UPDATE team_match_stats SET " +
194 "TeamNumber = ?, MatchNumber = ?, " +
195 "ShotsMissed = ?, UpperGoalShots = ?, LowerGoalShots = ?, " +
196 "ShotsMissedAuto = ?, UpperGoalAuto = ?, LowerGoalAuto = ?, " +
197 "PlayedDefense = ?, Climbing = ? " +
198 "WHERE MatchNumber = ? AND TeamNumber = ?")
199 if err != nil {
200 return errors.New(fmt.Sprint("Failed to prepare stats update statement: ", err))
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800201 }
Philipp Schrader83fc2722022-03-10 21:59:20 -0800202 defer statement.Close()
203
204 result, err := statement.Exec(s.TeamNumber, s.MatchNumber,
205 s.ShotsMissed, s.UpperGoalShots, s.LowerGoalShots,
206 s.ShotsMissedAuto, s.UpperGoalAuto, s.LowerGoalAuto,
207 s.PlayedDefense, s.Climbing,
208 s.MatchNumber, s.TeamNumber)
209 if err != nil {
210 return errors.New(fmt.Sprint("Failed to update stats database: ", err))
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800211 }
Philipp Schrader83fc2722022-03-10 21:59:20 -0800212
213 numRowsAffected, err := result.RowsAffected()
214 if err != nil {
215 return errors.New(fmt.Sprint("Failed to query rows affected: ", err))
Philipp Schrader30005e42022-03-06 13:53:58 -0800216 }
217 if numRowsAffected == 0 {
218 return errors.New(fmt.Sprint(
219 "Failed to find team ", s.TeamNumber,
220 " in match ", s.MatchNumber, " in the schedule."))
221 }
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800222 return nil
223}
224
225func (database *Database) ReturnMatches() ([]Match, error) {
Philipp Schrader83fc2722022-03-10 21:59:20 -0800226 rows, err := database.Query("SELECT * FROM matches")
227 if err != nil {
228 return nil, errors.New(fmt.Sprint("Failed to select from matches: ", err))
229 }
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800230 defer rows.Close()
Philipp Schrader83fc2722022-03-10 21:59:20 -0800231
232 matches := make([]Match, 0)
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800233 for rows.Next() {
234 var match Match
235 var id int
Philipp Schrader83fc2722022-03-10 21:59:20 -0800236 err := rows.Scan(&id, &match.MatchNumber, &match.Round, &match.CompLevel,
237 &match.R1, &match.R2, &match.R3, &match.B1, &match.B2, &match.B3,
238 &match.r1ID, &match.r2ID, &match.r3ID, &match.b1ID, &match.b2ID, &match.b3ID)
239 if err != nil {
240 return nil, errors.New(fmt.Sprint("Failed to scan from matches: ", err))
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800241 }
242 matches = append(matches, match)
243 }
244 return matches, nil
245}
246
247func (database *Database) ReturnStats() ([]Stats, error) {
Philipp Schrader83fc2722022-03-10 21:59:20 -0800248 rows, err := database.Query("SELECT * FROM team_match_stats")
249 if err != nil {
250 return nil, errors.New(fmt.Sprint("Failed to SELECT * FROM team_match_stats: ", err))
Philipp Schrader30005e42022-03-06 13:53:58 -0800251 }
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800252 defer rows.Close()
Philipp Schrader83fc2722022-03-10 21:59:20 -0800253
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800254 teams := make([]Stats, 0)
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800255 for rows.Next() {
256 var team Stats
Philipp Schrader83fc2722022-03-10 21:59:20 -0800257 var id int
258 err = rows.Scan(&id, &team.TeamNumber, &team.MatchNumber,
259 &team.ShotsMissed, &team.UpperGoalShots, &team.LowerGoalShots,
260 &team.ShotsMissedAuto, &team.UpperGoalAuto, &team.LowerGoalAuto,
261 &team.PlayedDefense, &team.Climbing)
262 if err != nil {
263 return nil, errors.New(fmt.Sprint("Failed to scan from stats: ", err))
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800264 }
265 teams = append(teams, team)
266 }
267 return teams, nil
268}
269
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800270func (database *Database) QueryMatches(teamNumber_ int32) ([]Match, error) {
Philipp Schrader83fc2722022-03-10 21:59:20 -0800271 rows, err := database.Query("SELECT * FROM matches WHERE "+
272 "R1 = ? OR R2 = ? OR R3 = ? OR B1 = ? OR B2 = ? OR B3 = ?",
273 teamNumber_, teamNumber_, teamNumber_, teamNumber_, teamNumber_, teamNumber_)
274 if err != nil {
275 return nil, errors.New(fmt.Sprint("Failed to select from matches for team: ", err))
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800276 }
277 defer rows.Close()
Philipp Schrader83fc2722022-03-10 21:59:20 -0800278
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800279 var matches []Match
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800280 for rows.Next() {
281 var match Match
Philipp Schrader83fc2722022-03-10 21:59:20 -0800282 var id int
283 err = rows.Scan(&id, &match.MatchNumber, &match.Round, &match.CompLevel,
284 &match.R1, &match.R2, &match.R3, &match.B1, &match.B2, &match.B3,
285 &match.r1ID, &match.r2ID, &match.r3ID, &match.b1ID, &match.b2ID, &match.b3ID)
286 if err != nil {
287 return nil, errors.New(fmt.Sprint("Failed to scan from matches: ", err))
288 }
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800289 matches = append(matches, match)
290 }
291 return matches, nil
292}
293
294func (database *Database) QueryStats(teamNumber_ int) ([]Stats, error) {
Philipp Schrader83fc2722022-03-10 21:59:20 -0800295 rows, err := database.Query("SELECT * FROM team_match_stats WHERE TeamNumber = ?", teamNumber_)
296 if err != nil {
297 return nil, errors.New(fmt.Sprint("Failed to select from stats: ", err))
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800298 }
299 defer rows.Close()
Philipp Schrader83fc2722022-03-10 21:59:20 -0800300
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800301 var teams []Stats
302 for rows.Next() {
303 var team Stats
304 var id int
Philipp Schrader83fc2722022-03-10 21:59:20 -0800305 err = rows.Scan(&id, &team.TeamNumber, &team.MatchNumber,
306 &team.ShotsMissed, &team.UpperGoalShots, &team.LowerGoalShots,
307 &team.ShotsMissedAuto, &team.UpperGoalAuto, &team.LowerGoalAuto,
308 &team.PlayedDefense, &team.Climbing)
309 if err != nil {
310 return nil, errors.New(fmt.Sprint("Failed to scan from stats: ", err))
311 }
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800312 teams = append(teams, team)
313 }
Sabina Leaverc5fd2772022-01-29 17:00:23 -0800314 return teams, nil
315}
Alex Perry871eab92022-03-12 17:43:52 -0800316
317func (database *Database) QueryNotes(TeamNumber int32) (NotesData, error) {
318 rows, err := database.Query("SELECT * FROM team_notes WHERE TeamNumber = ?", TeamNumber)
319 if err != nil {
320 return NotesData{}, errors.New(fmt.Sprint("Failed to select from notes: ", err))
321 }
322 defer rows.Close()
323
324 var notes []string
325 for rows.Next() {
326 var id int32
327 var data string
328 err = rows.Scan(&id, &TeamNumber, &data)
329 if err != nil {
330 return NotesData{}, errors.New(fmt.Sprint("Failed to scan from notes: ", err))
331 }
332 notes = append(notes, data)
333 }
334 return NotesData{TeamNumber, notes}, nil
335}
336
337func (database *Database) AddNotes(data NotesData) error {
338 if len(data.Notes) > 1 {
339 return errors.New("Can only insert one row of notes at a time")
340 }
341 statement, err := database.Prepare("INSERT INTO " +
342 "team_notes(TeamNumber, Notes)" +
343 "VALUES (?, ?)")
344 if err != nil {
345 return errors.New(fmt.Sprint("Failed to prepare insertion into notes table: ", err))
346 }
347 defer statement.Close()
348
349 _, err = statement.Exec(data.TeamNumber, data.Notes[0])
350 if err != nil {
351 return errors.New(fmt.Sprint("Failed to insert into Notes database: ", err))
352 }
353 return nil
354}