-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdatabase.nim
149 lines (114 loc) · 3.14 KB
/
database.nim
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
import std/[times, strutils, options]
import norm/[model, sqlite, pragmas]
import problem, tg
# --- types
type
Stats* = tuple
users, answered, free, total: int64
# --- models
type
UserState* = enum
usInitial
usProblem
usAnswer
usWon
User* = ref object of Model
chatid*{.unique.}: int64
username*: string
firstname*: string
lastname*: string
isAdmin*: bool
state*: UserState
Puzzle* = ref object of Model
initial*: string
logs*: string
shuffled*: string
belongs*: Option[User]
Attempt* = ref object of Model
user*: User
timestamp*: Datetime
succeed*: bool
# --- support for enum field
func dbType*(T: typedesc[enum]): string = "INTEGER"
func dbValue*(val: enum): DbValue = dbValue val.int
func to*(dbVal: DbValue, T: typedesc[enum]): T = dbVal.i.T
# --- utils
proc genPuzzle*(poet: string): Puzzle =
let (final, logs) = generateProblem(poet, 80 .. 100)
Puzzle(initial: poet, logs: reprLogs logs, shuffled: final)
# --- aliases
template `||`(code): untyped =
withDb code
proc update*(u: User, newState: UserState) =
var tmp = u
tmp.state = newState
|| db.update tmp
proc update*[M: Model](ins: M) =
var tmp = ins
|| db.update tmp
proc remove*[M: Model](instance: M) =
var tmp = instance
|| db.delete tmp
# --- actions
proc getUser*(chatid: int64): User =
result = User()
|| db.select(result, "chatid == ?", chatid)
proc getAdmins*: seq[User] =
result = @[User()]
|| db.select(result, "isAdmin")
proc addUser*(tid: int64, u, f, l: string, a = false): User =
result = User(
chatid: tid,
username: u,
firstname: f,
lastname: l,
isAdmin: a,
state: usInitial)
|| db.insert result
proc addOrGetUser*(chatid: int64, u, f, l: string): User =
try:
getUser(chatid)
except NotFoundError:
addUser(chatid, u, f, l)
proc addOrGetUser*(tu: TgUserInfo): User =
addOrGetUser(tu.chatid,
tu.username, tu.firstname, tu.lastname)
proc addPuzzle*(poet: string): Puzzle =
result = genPuzzle poet
|| db.insert result
proc getFreePuzzle*: Puzzle =
new result
|| db.select(result, "belongs IS NULL")
proc getUserPuzzle*(u: User): Puzzle =
result = Puzzle(belongs: some User())
|| db.select(result, "belongs == ?", u)
proc allPuzzles*: seq[Puzzle] =
result = @[Puzzle(belongs: some User())]
|| db.select(result, "1 = 1")
proc getWinners*: seq[User] =
result = @[User()]
|| db.select(result, "state = ?", usWon)
proc addAttempt*(u: User, c: bool): Attempt =
result = Attempt(user: u, succeed: c, timestamp: now())
|| db.insert result
proc getStats*: Stats =
withDb:
result.users = db.count(User)
result.answered = db.count(User, "*", false, "state == ?", usWon)
result.free = db.count(Puzzle, "*", false, "belongs IS NULL")
result.total = db.count(Puzzle)
proc resetUser*(u: sink User) =
withDb:
db.transaction:
u.state = usInitial
db.update u
try:
var p = getUserPuzzle(u)
reset p.belongs
db.update p
except NotFoundError: discard
except: rollback()
# --- general
proc createDB* =
|| db.createTables Attempt(user: User())
|| db.createTables Puzzle()