model.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955
  1. import random
  2. import re
  3. import sqlite3 as db
  4. import sys
  5. import uuid
  6. from datetime import timedelta, datetime
  7. from math import floor
  8. import db_setup
  9. from game import CURRENCY_NAME
  10. from util import random_chars
  11. from debug import debug
  12. # connection: db.Connection = None
  13. # cursor: db.Cursor = None
  14. connection = None # no type annotations in python 3.5
  15. cursor = None
  16. db_name = None
  17. def query_save_name():
  18. global db_name
  19. if debug:
  20. db_name = 'test.db'
  21. return
  22. while True:
  23. save_name = input('Name of the savegame: ')
  24. if re.match(r"[A-Za-z0-9.-]{0,50}", save_name):
  25. db_name = save_name + '.db'
  26. return
  27. else:
  28. print('Must match "[A-Za-z0-9.-]{0,50}"')
  29. def connect(reconnect=False):
  30. global connection
  31. global cursor
  32. global db_name
  33. if reconnect:
  34. connection.commit()
  35. connection.close()
  36. cursor = None
  37. connection = None
  38. db_name = None
  39. if connection is None or cursor is None:
  40. query_save_name()
  41. try:
  42. connection = db.connect(db_name)
  43. # connection.text_factory = lambda x: unicode(x, 'utf-8', 'ignore')
  44. cursor = connection.cursor()
  45. except db.Error as e:
  46. print("Database error %s:" % e.args[0])
  47. sys.exit(1)
  48. # finally:
  49. # if con is not None:
  50. # con.close()
  51. def setup():
  52. connect()
  53. db_setup.setup(cursor)
  54. connection.commit()
  55. def used_key_count():
  56. connect()
  57. cursor.execute('''
  58. SELECT COUNT(*)
  59. FROM keys
  60. WHERE used_by_user_id IS NOT NULL
  61. ''')
  62. return cursor.fetchone()[0]
  63. def login(username, password):
  64. connect()
  65. # do not allow login as bank
  66. if password == '':
  67. return None
  68. cursor.execute('''
  69. SELECT rowid
  70. FROM users
  71. WHERE username = ?
  72. AND password = ?
  73. ''', (username, password))
  74. user_id = cursor.fetchone()
  75. if user_id:
  76. return new_session(user_id)
  77. else:
  78. return None
  79. def register(username, password, game_key):
  80. connect()
  81. if username == '':
  82. return False
  83. if password == '':
  84. return False
  85. cursor.execute('''
  86. INSERT INTO users
  87. (username, password)
  88. VALUES (? , ?)
  89. ''', (username, password))
  90. own(get_user_id_by_name(username), CURRENCY_NAME)
  91. if game_key != '':
  92. if valid_key(game_key):
  93. activate_key(game_key, get_user_id_by_name(username))
  94. return True
  95. def own(user_id, ownable_name):
  96. if not isinstance(ownable_name, str):
  97. return AssertionError('A name must be a string.')
  98. cursor.execute('''
  99. WITH one_ownable_id AS (SELECT rowid FROM ownables WHERE name = ?),
  100. one_user_id AS (SELECT ?)
  101. INSERT INTO ownership (user_id, ownable_id)
  102. SELECT *
  103. FROM one_user_id, one_ownable_id
  104. WHERE NOT EXISTS (
  105. SELECT * FROM ownership
  106. WHERE ownership.user_id IN one_user_id
  107. AND ownership.ownable_id IN one_ownable_id
  108. )
  109. ''', (ownable_name, user_id,))
  110. def send_ownable(from_user_id, to_user_id, ownable_name, amount):
  111. connect()
  112. if amount < 0:
  113. return False
  114. if from_user_id != bank_id():
  115. cursor.execute('''
  116. UPDATE ownership
  117. SET amount = amount - ?
  118. WHERE user_id = ?
  119. AND ownable_id = (SELECT rowid FROM ownables WHERE name = ?)
  120. ''', (amount, from_user_id, ownable_name,))
  121. cursor.execute('''
  122. UPDATE ownership
  123. SET amount = amount + ?
  124. WHERE user_id = ?
  125. AND ownable_id = (SELECT rowid FROM ownables WHERE name = ?)
  126. ''', (amount, to_user_id, ownable_name))
  127. return True
  128. def valid_key(key):
  129. connect()
  130. cursor.execute('''
  131. SELECT key
  132. FROM keys
  133. WHERE used_by_user_id IS NULL
  134. AND key = ?
  135. ''', (key,))
  136. if cursor.fetchone():
  137. return True
  138. else:
  139. return False
  140. def new_session(user_id):
  141. connect()
  142. session_id = str(uuid.uuid4())
  143. cursor.execute('''
  144. INSERT INTO SESSIONS
  145. (user_id, session_id)
  146. VALUES (? , ?)
  147. ''', (user_id[0], session_id))
  148. return session_id
  149. def save_key(key):
  150. connect()
  151. cursor.execute('''
  152. INSERT INTO keys
  153. (key)
  154. VALUES (?)
  155. ''', (key,))
  156. def drop_old_sessions():
  157. connect()
  158. cursor.execute('''
  159. DELETE FROM sessions
  160. WHERE
  161. (SELECT COUNT(*) as newer
  162. FROM sessions s2
  163. WHERE user_id = s2.user_id
  164. AND rowid < s2.rowid) >= 10
  165. ''')
  166. def user_exists(username):
  167. connect()
  168. cursor.execute('''
  169. SELECT rowid
  170. FROM users
  171. WHERE username = ?
  172. ''', (username,))
  173. if cursor.fetchone():
  174. return True
  175. else:
  176. return False
  177. def unused_keys():
  178. connect()
  179. cursor.execute('''
  180. SELECT key
  181. FROM keys
  182. WHERE used_by_user_id IS NULL
  183. ''')
  184. return [str(key[0]).strip().upper() for key in cursor.fetchall()]
  185. def get_user_id_by_session_id(session_id):
  186. connect()
  187. cursor.execute('''
  188. SELECT users.rowid
  189. FROM sessions, users
  190. WHERE sessions.session_id = ?
  191. AND users.rowid = sessions.user_id
  192. ''', (session_id,))
  193. ids = cursor.fetchone()
  194. if not ids:
  195. return False
  196. return ids[0]
  197. def get_user_id_by_name(username):
  198. connect()
  199. cursor.execute('''
  200. SELECT users.rowid
  201. FROM users
  202. WHERE username = ?
  203. ''', (username,))
  204. return cursor.fetchone()[0]
  205. def get_user_ownership(user_id):
  206. connect()
  207. cursor.execute('''
  208. SELECT
  209. ownables.name,
  210. ownership.amount,
  211. COALESCE (
  212. CASE -- sum score for each of the users ownables
  213. WHEN ownership.ownable_id = ? THEN 1
  214. ELSE (SELECT price
  215. FROM transactions
  216. WHERE ownable_id = ownership.ownable_id
  217. ORDER BY dt DESC
  218. LIMIT 1)
  219. END, 0) AS value
  220. FROM ownership, ownables
  221. WHERE user_id = ?
  222. AND ownership.amount > 0
  223. AND ownership.ownable_id = ownables.rowid
  224. ''', (currency_id(), user_id,))
  225. return cursor.fetchall()
  226. def activate_key(key, user_id):
  227. connect()
  228. cursor.execute('''
  229. UPDATE keys
  230. SET used_by_user_id = ?
  231. WHERE used_by_user_id IS NULL
  232. AND key = ?
  233. ''', (user_id, key,))
  234. send_ownable(bank_id(), user_id, CURRENCY_NAME, 1000)
  235. def bank_id():
  236. connect()
  237. cursor.execute('''
  238. SELECT users.rowid
  239. FROM users
  240. WHERE username = 'bank'
  241. ''')
  242. return cursor.fetchone()[0]
  243. def valid_session_id(session_id):
  244. connect()
  245. cursor.execute('''
  246. SELECT rowid
  247. FROM sessions
  248. WHERE session_id = ?
  249. ''', (session_id,))
  250. if cursor.fetchone():
  251. return True
  252. else:
  253. return False
  254. def get_user_orders(user_id):
  255. connect()
  256. cursor.execute('''
  257. SELECT
  258. CASE
  259. WHEN orders.buy THEN 'Buy'
  260. ELSE 'Sell'
  261. END,
  262. ownables.name,
  263. orders.ordered_amount - orders.executed_amount,
  264. orders."limit",
  265. CASE
  266. WHEN orders."limit" IS NULL THEN NULL
  267. WHEN orders.stop_loss THEN 'Yes'
  268. ELSE 'No'
  269. END,
  270. orders.ordered_amount,
  271. datetime(orders.expiry_dt),
  272. orders.rowid
  273. FROM orders, ownables, ownership
  274. WHERE ownership.user_id = ?
  275. AND ownership.ownable_id = ownables.rowid
  276. AND orders.ownership_id = ownership.rowid
  277. ORDER BY ownables.name ASC, orders.stop_loss ASC, orders.buy DESC, orders."limit" ASC
  278. ''', (user_id,))
  279. return cursor.fetchall()
  280. def get_ownable_orders(ownable_id):
  281. connect()
  282. cursor.execute('''
  283. SELECT
  284. CASE
  285. WHEN orders.buy THEN 'Buy'
  286. ELSE 'Sell'
  287. END,
  288. ownables.name,
  289. orders.ordered_amount - orders.executed_amount,
  290. orders."limit",
  291. CASE
  292. WHEN orders."limit" IS NULL THEN NULL
  293. WHEN orders.stop_loss THEN 'Yes'
  294. ELSE 'No'
  295. END,
  296. datetime(orders.expiry_dt),
  297. orders.rowid
  298. FROM orders, ownables, ownership
  299. WHERE ownership.ownable_id = ?
  300. AND ownership.ownable_id = ownables.rowid
  301. AND orders.ownership_id = ownership.rowid
  302. ORDER BY ownables.name ASC, orders.stop_loss ASC, orders.buy DESC, orders."limit" ASC
  303. ''', (ownable_id,))
  304. return cursor.fetchall()
  305. def sell_ordered_amount(user_id, ownable_id):
  306. connect()
  307. # if ownable_id == currency_id():
  308. # return 0
  309. cursor.execute('''
  310. SELECT COALESCE(SUM(orders.ordered_amount - orders.executed_amount),0)
  311. FROM orders, ownership
  312. WHERE ownership.rowid = orders.ownership_id
  313. AND ownership.user_id = ?
  314. AND ownership.ownable_id = ?
  315. AND NOT orders.buy
  316. ''', (user_id, ownable_id))
  317. return cursor.fetchone()[0]
  318. def user_owns_at_least(amount, user_id, ownable_id):
  319. connect()
  320. if not isinstance(amount, float) and not isinstance(amount, int):
  321. # comparison of float with strings does not work so well in sql
  322. raise AssertionError()
  323. cursor.execute('''
  324. SELECT rowid
  325. FROM ownership
  326. WHERE user_id = ?
  327. AND ownable_id = ?
  328. AND amount - ? >= ?
  329. ''', (user_id, ownable_id, sell_ordered_amount(user_id, ownable_id), amount))
  330. if cursor.fetchone():
  331. return True
  332. else:
  333. return False
  334. def news():
  335. connect()
  336. cursor.execute('''
  337. SELECT * FROM
  338. (SELECT *
  339. FROM news
  340. ORDER BY dt DESC
  341. LIMIT 20) n
  342. ORDER BY dt ASC
  343. ''')
  344. return cursor.fetchall()
  345. def ownable_name_exists(name):
  346. connect()
  347. cursor.execute('''
  348. SELECT rowid
  349. FROM ownables
  350. WHERE name = ?
  351. ''', (name,))
  352. if cursor.fetchone():
  353. return True
  354. else:
  355. return False
  356. def new_stock(timeout=60, name=None):
  357. connect()
  358. while name is None:
  359. name = random_chars(6)
  360. if ownable_name_exists(name):
  361. name = None
  362. cursor.execute('''
  363. INSERT INTO ownables(name)
  364. VALUES (?)
  365. ''', (name,))
  366. cursor.execute('''
  367. INSERT INTO news(title)
  368. VALUES (?)
  369. ''', ('A new stock can now be bought: ' + name,))
  370. if random.getrandbits(1):
  371. cursor.execute('''
  372. INSERT INTO news(title)
  373. VALUES (?)
  374. ''', ('Experts expect the price of ' + name + ' to fall',))
  375. else:
  376. cursor.execute('''
  377. INSERT INTO news(title)
  378. VALUES (?)
  379. ''', ('Experts expect the price of ' + name + ' to rise',))
  380. amount = random.randrange(100, 10000)
  381. price = random.randrange(10000, 20000) / amount
  382. ownable_id = ownable_id_by_name(name)
  383. own(bank_id(), name)
  384. bank_order(False,
  385. ownable_id,
  386. price,
  387. amount,
  388. timeout)
  389. return name
  390. def new_stocks(timeout=60, count=1):
  391. return [new_stock(timeout=timeout) for _ in range(count)]
  392. def ownable_id_by_name(ownable_name):
  393. connect()
  394. cursor.execute('''
  395. SELECT rowid
  396. FROM ownables
  397. WHERE name = ?
  398. ''', (ownable_name,))
  399. return cursor.fetchone()[0]
  400. def get_ownership_id(ownable_id, user_id):
  401. connect()
  402. cursor.execute('''
  403. SELECT rowid
  404. FROM ownership
  405. WHERE ownable_id = ?
  406. AND user_id = ?
  407. ''', (ownable_id, user_id,))
  408. return cursor.fetchone()[0]
  409. def currency_id():
  410. connect()
  411. cursor.execute('''
  412. SELECT rowid
  413. FROM ownables
  414. WHERE name = ?
  415. ''', (CURRENCY_NAME,))
  416. return cursor.fetchone()[0]
  417. def user_money(user_id):
  418. connect()
  419. cursor.execute('''
  420. SELECT amount
  421. FROM ownership
  422. WHERE user_id = ?
  423. AND ownable_id = ?
  424. ''', (user_id, currency_id()))
  425. return cursor.fetchone()[0]
  426. def delete_order(order_id):
  427. connect()
  428. cursor.execute('''
  429. DELETE FROM orders
  430. WHERE rowid = ?
  431. ''', (order_id,))
  432. def current_value(ownable_id):
  433. connect()
  434. cursor.execute('''SELECT price
  435. FROM transactions
  436. WHERE ownable_id = ?
  437. ORDER BY dt DESC
  438. LIMIT 1
  439. ''', (ownable_id,))
  440. return cursor.fetchone()[0]
  441. def execute_orders(ownable_id):
  442. connect()
  443. while True:
  444. # find order to execute
  445. cursor.execute('''
  446. SELECT buy_order.*, sell_order.*, buyer.user_id, seller.user_id, buy_order.rowid, sell_order.rowid
  447. FROM orders buy_order, orders sell_order, ownership buyer, ownership seller
  448. WHERE buy_order.buy AND NOT sell_order.buy
  449. AND buyer.rowid = buy_order.ownership_id
  450. AND seller.rowid = sell_order.ownership_id
  451. AND buyer.ownable_id = ?
  452. AND seller.ownable_id = ?
  453. AND (buy_order."limit" IS NULL
  454. OR sell_order."limit" IS NULL
  455. OR (sell_order."limit" <= buy_order."limit"
  456. AND NOT sell_order.stop_loss
  457. AND NOT buy_order.stop_loss))
  458. ORDER BY CASE WHEN sell_order."limit" IS NULL THEN 0 ELSE 1 END ASC,
  459. CASE WHEN buy_order."limit" IS NULL THEN 0 ELSE 1 END ASC,
  460. buy_order."limit" DESC,
  461. sell_order."limit" ASC,
  462. buy_order.ordered_amount - buy_order.executed_amount DESC,
  463. sell_order.ordered_amount - sell_order.executed_amount DESC
  464. LIMIT 1
  465. ''', (ownable_id, ownable_id,))
  466. matching_orders = cursor.fetchone()
  467. # return type: (ownership_id,buy,limit,stop_loss,ordered_amount,executed_amount,expiry_dt,
  468. # ownership_id,buy,limit,stop_loss,ordered_amount,executed_amount,expiry_dt,
  469. # user_id,user_id,rowid,rowid)
  470. if not matching_orders:
  471. break
  472. buy_ownership_id, _, buy_limit, _, buy_order_amount, buy_executed_amount, buy_expiry_dt, \
  473. sell_ownership_id, _, sell_limit, _, sell_order_amount, sell_executed_amount, sell_expiry_dt, \
  474. buyer_id, seller_id, buy_order_id, sell_order_id \
  475. = matching_orders
  476. if buy_limit is None and sell_limit is None:
  477. price = current_value(ownable_id)
  478. elif buy_limit is None:
  479. price = sell_limit
  480. elif sell_limit is None:
  481. price = buy_limit
  482. else: # both not NULL
  483. price = (float(sell_limit) + float(buy_limit)) / 2
  484. if price == 0:
  485. raise AssertionError()
  486. buyer_money = user_money(buyer_id)
  487. amount = min(buy_order_amount - buy_executed_amount,
  488. sell_order_amount - sell_executed_amount,
  489. floor(buyer_money / price))
  490. if amount == 0: # probable because buyer has not enough money
  491. delete_order(buy_order_id)
  492. continue
  493. buy_order_finished = (buy_order_amount - buy_executed_amount - amount <= 0) or (
  494. buyer_money - amount * price < price)
  495. sell_order_finished = (sell_order_amount - sell_executed_amount - amount <= 0)
  496. if price < 0 or amount <= 0:
  497. return AssertionError()
  498. # actually execute the order, but the bank does not send or receive anything
  499. if buyer_id != bank_id(): # buyer pays
  500. cursor.execute('''
  501. UPDATE ownership
  502. SET amount = amount - ?
  503. WHERE user_id = ?
  504. AND ownable_id = ?
  505. ''', (price * amount, buyer_id, currency_id()))
  506. if seller_id != bank_id(): # seller pays
  507. cursor.execute('''
  508. UPDATE ownership
  509. SET amount = amount - ?
  510. WHERE rowid = ?
  511. ''', (amount, sell_ownership_id))
  512. if buyer_id != bank_id(): # buyer receives
  513. cursor.execute('''
  514. UPDATE ownership
  515. SET amount = amount + ?
  516. WHERE rowid = ?
  517. ''', (amount, buy_ownership_id))
  518. if seller_id != bank_id(): # seller receives
  519. cursor.execute('''
  520. UPDATE ownership
  521. SET amount = amount + ?
  522. WHERE user_id = ?
  523. AND ownable_id = ?
  524. ''', (price * amount, seller_id, currency_id()))
  525. # update order execution state
  526. cursor.execute('''
  527. UPDATE orders
  528. SET executed_amount = executed_amount + ?
  529. WHERE rowid = ?
  530. OR rowid = ?
  531. ''', (amount, buy_order_id, sell_order_id))
  532. if buy_order_finished:
  533. delete_order(buy_order_id)
  534. if sell_order_finished:
  535. delete_order(sell_order_id)
  536. if seller_id != buyer_id: # prevent showing self-transactions
  537. cursor.execute('''
  538. INSERT INTO transactions
  539. (price, ownable_id, amount)
  540. VALUES(?, ?, ?)
  541. ''', (price, ownable_id, amount,))
  542. # trigger stop loss orders
  543. if buyer_id != seller_id:
  544. cursor.execute('''
  545. UPDATE orders
  546. SET stop_loss = NULL,
  547. "limit" = NULL
  548. WHERE stop_loss IS NOT NULL
  549. AND stop_loss
  550. AND ? IN (SELECT ownable_id FROM ownership WHERE rowid = ownership_id)
  551. AND ((buy AND "limit" < ?) OR (NOT buy AND "limit" > ?))
  552. ''', (ownable_id, price, price,))
  553. def ownable_id_by_ownership_id(ownership_id):
  554. connect()
  555. cursor.execute('''
  556. SELECT ownable_id
  557. FROM ownership
  558. WHERE rowid = ?
  559. ''', (ownership_id,))
  560. return cursor.fetchone()[0]
  561. def ownable_name_by_id(ownable_id):
  562. connect()
  563. cursor.execute('''
  564. SELECT name
  565. FROM ownables
  566. WHERE rowid = ?
  567. ''', (ownable_id,))
  568. return cursor.fetchone()[0]
  569. def bank_order(buy, ownable_id, limit, amount, time_until_expiration):
  570. if not limit:
  571. raise AssertionError('The bank does not give away anything.')
  572. place_order(buy,
  573. get_ownership_id(ownable_id, bank_id()),
  574. limit,
  575. False,
  576. amount,
  577. time_until_expiration)
  578. ownable_name = ownable_name_by_id(ownable_id)
  579. cursor.execute('''
  580. INSERT INTO news(title)
  581. VALUES (?)
  582. ''', ('External investors are selling ' + ownable_name + ' atm',))
  583. def current_time(): # might differ from datetime.datetime.now() for time zone reasons
  584. connect()
  585. cursor.execute('''
  586. SELECT datetime('now')
  587. ''')
  588. return cursor.fetchone()[0]
  589. def place_order(buy, ownership_id, limit, stop_loss, amount, time_until_expiration):
  590. connect()
  591. expiry = datetime.strptime(current_time(), '%Y-%m-%d %H:%M:%S') + timedelta(minutes=time_until_expiration)
  592. cursor.execute('''
  593. INSERT INTO orders
  594. (buy, ownership_id, "limit", stop_loss, ordered_amount, expiry_dt)
  595. VALUES (?, ?, ?, ?, ?, ?)
  596. ''', (buy, ownership_id, limit, stop_loss, amount, expiry))
  597. execute_orders(ownable_id_by_ownership_id(ownership_id))
  598. return True
  599. def transactions(ownable_id):
  600. connect()
  601. cursor.execute('''
  602. SELECT dt, amount, price
  603. FROM transactions
  604. WHERE ownable_id = ?
  605. ORDER BY dt DESC
  606. ''', (ownable_id,))
  607. return cursor.fetchall()
  608. def drop_expired_orders():
  609. connect()
  610. cursor.execute('''
  611. DELETE FROM orders
  612. WHERE expiry_dt < DATETIME('now')
  613. ''')
  614. return cursor.fetchall()
  615. def generate_keys(count=1):
  616. # source https://stackoverflow.com/questions/17049308/python-3-3-serial-key-generator-list-problems
  617. for i in range(count):
  618. key = '-'.join(random_chars(5) for _ in range(5))
  619. save_key(key)
  620. print(key)
  621. def user_has_order_with_id(session_id, order_id):
  622. connect()
  623. cursor.execute('''
  624. SELECT orders.rowid
  625. FROM orders, ownership, sessions
  626. WHERE orders.rowid = ?
  627. AND sessions.session_id = ?
  628. AND sessions.user_id = ownership.user_id
  629. AND ownership.rowid = orders.ownership_id
  630. ''', (order_id, session_id,))
  631. if cursor.fetchone():
  632. return True
  633. else:
  634. return False
  635. def leaderboard():
  636. connect()
  637. cursor.execute('''
  638. SELECT *
  639. FROM ( -- one score for each user
  640. SELECT
  641. username,
  642. SUM(CASE -- sum score for each of the users ownables
  643. WHEN ownership.ownable_id = ? THEN ownership.amount
  644. ELSE ownership.amount * (SELECT price
  645. FROM transactions
  646. WHERE ownable_id = ownership.ownable_id
  647. ORDER BY dt DESC
  648. LIMIT 1)
  649. END
  650. ) score
  651. FROM users, ownership
  652. WHERE ownership.user_id = users.rowid
  653. AND users.username != 'bank'
  654. GROUP BY users.rowid
  655. ) AS scores
  656. ORDER BY score DESC
  657. LIMIT 50
  658. ''', (currency_id(),))
  659. return cursor.fetchall()
  660. def user_wealth(user_id):
  661. connect()
  662. cursor.execute('''
  663. SELECT SUM(
  664. CASE -- sum score for each of the users ownables
  665. WHEN ownership.ownable_id = ? THEN ownership.amount
  666. ELSE ownership.amount * (SELECT price
  667. FROM transactions
  668. WHERE ownable_id = ownership.ownable_id
  669. ORDER BY dt DESC
  670. LIMIT 1)
  671. END
  672. ) score
  673. FROM ownership
  674. WHERE ownership.user_id = ?
  675. ''', (currency_id(), user_id,))
  676. return cursor.fetchone()[0]
  677. def change_password(session_id, password):
  678. connect()
  679. cursor.execute('''
  680. UPDATE users
  681. SET password = ?
  682. WHERE ? IN (SELECT session_id FROM sessions WHERE sessions.user_id = users.rowid)
  683. ''', (password, session_id,))
  684. def sign_out_user(session_id):
  685. connect()
  686. cursor.execute('''
  687. DELETE FROM sessions
  688. WHERE user_id = (SELECT user_id FROM sessions s2 WHERE s2.session_id = ?)
  689. ''', (session_id,))
  690. def delete_user(user_id):
  691. connect()
  692. cursor.execute('''
  693. DELETE FROM sessions
  694. WHERE user_id = ?
  695. ''', (user_id,))
  696. cursor.execute('''
  697. DELETE FROM orders
  698. WHERE ownership_id IN (
  699. SELECT rowid FROM ownership WHERE user_id = ?)
  700. ''', (user_id,))
  701. cursor.execute('''
  702. DELETE FROM ownership
  703. WHERE user_id = ?
  704. ''', (user_id,))
  705. cursor.execute('''
  706. DELETE FROM keys
  707. WHERE used_by_user_id = ?
  708. ''', (user_id,))
  709. cursor.execute('''
  710. INSERT INTO news(title)
  711. VALUES ((SELECT username FROM users WHERE rowid = ?) || ' retired.')
  712. ''', (user_id,))
  713. cursor.execute('''
  714. DELETE FROM users
  715. WHERE rowid = ?
  716. ''', (user_id,))
  717. def delete_ownable(ownable_id):
  718. connect()
  719. cursor.execute('''
  720. DELETE FROM transactions
  721. WHERE ownable_id = ?
  722. ''', (ownable_id,))
  723. cursor.execute('''
  724. DELETE FROM orders
  725. WHERE ownership_id IN (
  726. SELECT rowid FROM ownership WHERE ownable_id = ?)
  727. ''', (ownable_id,))
  728. # only delete empty ownerships
  729. cursor.execute('''
  730. DELETE FROM ownership
  731. WHERE ownable_id = ?
  732. AND amount = 0
  733. ''', (ownable_id,))
  734. cursor.execute('''
  735. INSERT INTO news(title)
  736. VALUES ((SELECT name FROM ownables WHERE rowid = ?) || ' can not be traded any more.')
  737. ''', (ownable_id,))
  738. cursor.execute('''
  739. DELETE FROM ownables
  740. WHERE rowid = ?
  741. ''', (ownable_id,))