-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathdatabase.py
261 lines (251 loc) · 7.42 KB
/
database.py
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# pylint: disable=broad-except, unspecified-encoding, too-many-branches, no-self-use
"""
╔╗ ┬┌┬┐┌─┐┬ ┬┌─┐┬─┐┌─┐┌─┐ ╔╦╗┌─┐─┐ ┬ ╦ ╦─┐ ┬
╠╩╗│ │ └─┐├─┤├─┤├┬┘├┤ └─┐ ║║├┤ ┌┴┬┘ ║ ║┌┴┬┘
╚═╝┴ ┴ └─┘┴ ┴┴ ┴┴└─└─┘└─┘ ═╩╝└─┘┴ └─ ╚═╝┴ └─
Bitshares Decentralized Exchange User Experience
Initalize database and provide simple interface for accessing database
"""
# STANDARD MOUDLES
import json
import os
import time
from sqlite3 import Row, connect
# GLOBAL CONSTANTS
DEV = False
PATH = os.path.dirname(os.path.abspath(__file__)) + "/database"
CREATES = [
"""
CREATE TABLE nodes (
url TEXT PRIMARY KEY,
ping REAL,
handshake REAL,
blocktime INT,
code INT,
status TEXT
)
""",
"""
CREATE TABLE klines (
pair TEXT PRIMARY KEY,
end_unix INT,
c86400 TEXT,
c43200 TEXT,
c14400 TEXT,
c7200 TEXT,
c3600 TEXT,
c1800 TEXT,
c900 TEXT,
discrete TEXT
)
""",
"""
CREATE TABLE assets (
id TEXT PRIMARY KEY,
dynamic_id TEXT,
pool_id TEXT,
bitasset_id TEXT,
symbol TEXT NOT NULL UNIQUE,
precision INT,
maker_fee DECIMAL,
taker_fee DECIMAL,
description TEXT
)
""",
"""
CREATE TABLE pools (
id TEXT PRIMARY KEY,
asset_a TEXT,
asset_b TEXT,
asset_a_name TEXT,
asset_b_name TEXT,
balance_a DECIMAL,
balance_b DECIMAL,
share_asset TEXT,
share_asset_name TEXT,
taker_fee_percent DECIMAL,
withdrawal_fee_percent DECIMAL,
virtual_value DECIMAL,
pair TEXT,
xyk TEXT
)
""",
"""
CREATE TABLE accounts (
account_id TEXT PRIMARY KEY,
account_name TEXT,
is_ltm BOOL
)
""",
]
SELECTS = [
"""
SELECT * FROM nodes
""",
"""
SELECT * FROM assets
""",
"""
SELECT * FROM pools
""",
# """
# SELECT * FROM bitassets
# """,
"""
SELECT * FROM accounts
""",
]
UPDATES = [
(
"""
UPDATE nodes SET ping=?, code=?, status=?
""",
("999.9", "1000", "INITIALIZING"),
),
]
DATABASE = PATH + "/test"
ASSETS = ["HONEST.USD", "BTS"]
NODES = ["wss://api.bts.mobi/wss"]
PAIRS = ["HONEST.USD:BTS"]
class Sql:
"""
creation of graphene database and execution of queries
"""
def restart(self):
"""
delete any existing db and initialize new SQL db
"""
# create database folder
os.makedirs(PATH, exist_ok=True)
# user input w/ warning
print("\033c")
print("WARNING THIS SCRIPT WILL RESTART DATABASE AND ERASE ALL DATA\n")
# erase the database
command = f"rm {DATABASE}"
print("\033c", command, "\n")
os.system(command)
print("creating sqlite3:", DATABASE, "\n")
# initialize insert operations with chain specific configuration
inserts = []
for node in NODES:
inserts.append(
(
"""
INSERT INTO nodes (url) VALUES (?)
""",
(node,),
)
)
# new table creation
queries = []
for query in CREATES:
dml = {"query": query, "values": tuple()}
queries.append(dml)
self.execute(queries)
# row creation in each table
queries = []
for insert in inserts:
dml = {"query": insert[0], "values": insert[1]}
queries.append(dml)
self.execute(queries)
# default column data in each row
queries = []
for update in UPDATES:
dml = {"query": update[0], "values": update[1]}
queries.append(dml)
self.execute(queries)
# print
for query in SELECTS:
print(query, self.execute(query))
def execute(self, query, values=()):
"""
execute discrete sql queries, handle race condition gracefully
if query is a string, assume values is a
else, query can be a list of dicts with keys ["query","values"]
While True:
Try:
con = connect(DB)
cur = con.cursor()
cur.execute(query, values)
ret = cur.fetchall()
con.commit()
con.close()
break
Except:
continue
:return ret:
"""
queries = []
# handle both single query and multiple queries
if isinstance(query, str):
queries.append({"query": query, "values": values})
else:
queries = query
# strip double spaces and new lines in each query
for idx, dml in enumerate(queries):
queries[idx]["query"] = " ".join(dml["query"].replace("\n", " ").split())
# print sql except when...
for dml in queries:
if DEV:
print(f"'query': {dml['query']}")
print(f"'values': {dml['values']}\n")
# attempt to update database until satisfied
pause = -1
curfetchall = None
data = None
while True:
try:
pause += 1
# only allow batched write queries
if len(queries) > 1:
for dml in queries:
if "SELECT" in dml["query"]:
raise ValueError("batch queries must be write only")
# ======================================================================
# SQL CONNECT
# ======================================================================
con = connect(DATABASE)
for dml in queries:
con.row_factory = Row
cur = con.cursor()
cur.execute(dml["query"], dml["values"])
curfetchall = cur.fetchall()
# print(curfetchall)
con.commit()
con.close()
# ======================================================================
# SQL CLOSE
# ======================================================================
# print(curfetchall)
data = [dict(i) for i in curfetchall]
for idx, row in enumerate(data):
for key, val in row.items():
# attempt to load as JSON
try:
data[idx][key] = json.loads(val)
# otherwise give raw
except Exception:
data[idx][key] = val
return data
except Exception as error:
print(error)
print("Race condition at", int(time.time()))
try:
print(dml)
except Exception:
pass
# raise
# ascending pause here prevents excess cpu on corruption of database
# and allows for decreased load during race condition
time.sleep(min(5, 0.01))
continue
return data
def unit_test():
"""
initialize the database
"""
print("\033c")
sql = Sql()
sql.restart()
if __name__ == "__main__":
unit_test()