-
Notifications
You must be signed in to change notification settings - Fork 0
/
data.go
234 lines (196 loc) · 4.19 KB
/
data.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
package main
import (
"database/sql"
"fmt"
"log"
"math/rand"
"os"
"strconv"
"strings"
tea "github.com/charmbracelet/bubbletea"
_ "github.com/mattn/go-sqlite3"
)
var db *sql.DB
func closeDB() {
db.Close()
}
type Episode struct {
season int
episode int
number int
}
type Show struct {
name string
episodeCount int
seasonCount int
seasonLengths []int
}
func newShow() Show {
return Show{}
}
func (s Show) save() error {
if db == nil {
return fmt.Errorf("No db connection")
}
sl := ""
for _, l := range s.seasonLengths {
sl += fmt.Sprintf("%d,", l)
}
stmt := `INSERT INTO series (name, season_lengths) VALUES (?, ?);`
fmt.Println(stmt)
_, err := db.Exec(stmt, s.name, sl)
if err != nil {
return err
}
return nil
}
func (s Show) delete() error {
stmt := `UPDATE OR IGNORE series SET deleted = 1 WHERE name = ?`
_, err := db.Exec(stmt, s.name)
return err
}
func (s Show) undelete() error {
stmt := `UPDATE OR IGNORE series SET deleted = 0 WHERE name = ?`
_, err := db.Exec(stmt, s.name)
return err
}
func (s Show) getEpisode() (Episode, error) {
for i := 0; i < 50; i++ {
n := rand.Intn(s.episodeCount)
stmt := `SELECT number FROM episodes WHERE series = ? AND number = ? AND watched = 1;`
rows := db.QueryRow(stmt, s.name, n)
if rows.Scan() == sql.ErrNoRows {
m := n
for j, l := range s.seasonLengths {
if m < l {
return Episode{
season: j + 1,
episode: m + 1,
number: n,
}, nil
}
m -= l
}
}
}
return Episode{}, fmt.Errorf("No episode found")
}
func (s Show) watchEpisode(e Episode) error {
stmt := `INSERT INTO episodes (series, number, watched) VALUES (?, ?, 1);`
_, err := db.Exec(stmt, s.name, e.number)
return err
}
func showFromRow(rows *sql.Rows) (Show, error) {
var name string
var seasonLengths string
err := rows.Scan(&name, &seasonLengths)
if err != nil {
return Show{}, err
}
s := newShow()
s.name = name
for _, l := range strings.Split(seasonLengths, ",") {
n, err := strconv.Atoi(l)
if err == nil {
s.seasonLengths = append(s.seasonLengths, n)
s.episodeCount += n
}
}
return s, nil
}
// Connect to databse and return a Msg current shows from the db
func connectDB() tea.Msg {
dbDir := os.Getenv("XDG_DATA_HOME")
if dbDir == "" {
dbDir = os.Getenv("HOME") + "/.local/share"
}
dbDir = dbDir + "/random-episode"
os.MkdirAll(dbDir, 0755)
// Connect sqlite db
var err error
db, err = sql.Open("sqlite3", dbDir+"/data.db")
if err != nil {
log.Fatal(err)
}
err = alterDB()
if err != nil {
log.Fatal(err)
}
err = clearDeleted()
if err != nil {
log.Fatal(err)
}
return readShows()
}
func clearDeleted() error {
stmt := `DELETE FROM episodes WHERE watched = 0`
_, err := db.Exec(stmt)
if err != nil {
return err
}
// TODO: add foreign keys
stmt = `DELETE FROM episodes WHERE series IN (SELECT name FROM series WHERE deleted = 1)`
_, err = db.Exec(stmt)
if err != nil {
return err
}
stmt = `DELETE FROM series WHERE deleted = 1`
_, err = db.Exec(stmt)
return err
}
// Check database schema version and apply new changes if needed
func alterDB() error {
stmt := "PRAGMA user_version"
var version int
err := db.QueryRow(stmt).Scan(&version)
if err != nil {
return err
}
if version < 1 {
stmt = `CREATE TABLE IF NOT EXISTS series
(name text, season_lengths text);`
_, err = db.Exec(stmt)
if err != nil {
return err
}
stmt = `CREATE TABLE IF NOT EXISTS episodes
(series text, number int, watched int);`
_, err = db.Exec(stmt)
if err != nil {
return err
}
}
if version < 2 {
stmt = `ALTER TABLE series add COLUMN deleted int DEFAULT 0`
_, err = db.Exec(stmt)
if err != nil {
return err
}
}
if version < 2 { // MAKE SURE YOU UPDATE THIS ON CHANGE
stmt := "PRAGMA user_version = 2" // MAKE SURE YOU UPDATE THIS ON CHANGE
_, err = db.Exec(stmt)
if err != nil {
return err
}
}
return nil
}
func readShows() tea.Msg {
stmt := `SELECT name, season_lengths FROM series;`
rows, err := db.Query(stmt)
if err != nil {
log.Fatal(err)
}
shows := []Show{}
for rows.Next() {
s, err := showFromRow(rows)
if err != nil {
log.Print(err)
}
shows = append(shows, s)
}
return showsLoaded{
shows: shows,
}
}