model.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  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 *
  338. FROM news
  339. ORDER BY dt DESC
  340. LIMIT 20
  341. ''')
  342. return cursor.fetchall()
  343. def ownable_name_exists(name):
  344. connect()
  345. cursor.execute('''
  346. SELECT rowid
  347. FROM ownables
  348. WHERE name = ?
  349. ''', (name,))
  350. if cursor.fetchone():
  351. return True
  352. else:
  353. return False
  354. def new_stock(timeout=60, name=None):
  355. connect()
  356. while name is None:
  357. name = random_chars(6)
  358. if ownable_name_exists(name):
  359. name = None
  360. cursor.execute('''
  361. INSERT INTO ownables(name)
  362. VALUES (?)
  363. ''', (name,))
  364. cursor.execute('''
  365. INSERT INTO news(title)
  366. VALUES (?)
  367. ''', ('A new stock can now be bought: ' + name,))
  368. if random.getrandbits(1):
  369. cursor.execute('''
  370. INSERT INTO news(title)
  371. VALUES (?)
  372. ''', ('Experts expect the price of ' + name + ' to fall',))
  373. else:
  374. cursor.execute('''
  375. INSERT INTO news(title)
  376. VALUES (?)
  377. ''', ('Experts expect the price of ' + name + ' to rise',))
  378. amount = random.randrange(100, 10000)
  379. price = random.randrange(10000, 20000) / amount
  380. ownable_id = ownable_id_by_name(name)
  381. own(bank_id(), name)
  382. bank_order(False,
  383. ownable_id,
  384. price,
  385. amount,
  386. timeout)
  387. return name
  388. def new_stocks(timeout=60, count=1):
  389. return [new_stock(timeout=timeout) for _ in range(count)]
  390. def ownable_id_by_name(ownable_name):
  391. connect()
  392. cursor.execute('''
  393. SELECT rowid
  394. FROM ownables
  395. WHERE name = ?
  396. ''', (ownable_name,))
  397. return cursor.fetchone()[0]
  398. def get_ownership_id(ownable_id, user_id):
  399. connect()
  400. cursor.execute('''
  401. SELECT rowid
  402. FROM ownership
  403. WHERE ownable_id = ?
  404. AND user_id = ?
  405. ''', (ownable_id, user_id,))
  406. return cursor.fetchone()[0]
  407. def currency_id():
  408. connect()
  409. cursor.execute('''
  410. SELECT rowid
  411. FROM ownables
  412. WHERE name = ?
  413. ''', (CURRENCY_NAME,))
  414. return cursor.fetchone()[0]
  415. def user_money(user_id):
  416. connect()
  417. cursor.execute('''
  418. SELECT amount
  419. FROM ownership
  420. WHERE user_id = ?
  421. AND ownable_id = ?
  422. ''', (user_id, currency_id()))
  423. return cursor.fetchone()[0]
  424. def delete_order(order_id):
  425. connect()
  426. cursor.execute('''
  427. DELETE FROM orders
  428. WHERE rowid = ?
  429. ''', (order_id,))
  430. def execute_orders(ownable_id):
  431. connect()
  432. while True:
  433. # find order to execute
  434. cursor.execute('''
  435. SELECT buy_order.*, sell_order.*, buyer.user_id, seller.user_id, buy_order.rowid, sell_order.rowid
  436. FROM orders buy_order, orders sell_order, ownership buyer, ownership seller
  437. WHERE buy_order.buy AND NOT sell_order.buy
  438. AND buyer.rowid = buy_order.ownership_id
  439. AND seller.rowid = sell_order.ownership_id
  440. AND buyer.ownable_id = ?
  441. AND seller.ownable_id = ?
  442. AND (buy_order."limit" IS NULL
  443. OR sell_order."limit" IS NULL
  444. OR (sell_order."limit" <= buy_order."limit"
  445. AND NOT sell_order.stop_loss
  446. AND NOT buy_order.stop_loss))
  447. ORDER BY CASE WHEN sell_order."limit" IS NULL THEN 0 ELSE 1 END ASC,
  448. CASE WHEN buy_order."limit" IS NULL THEN 0 ELSE 1 END ASC,
  449. buy_order."limit" DESC,
  450. sell_order."limit" ASC,
  451. buy_order.ordered_amount - buy_order.executed_amount DESC,
  452. sell_order.ordered_amount - sell_order.executed_amount DESC
  453. LIMIT 1
  454. ''', (ownable_id, ownable_id,))
  455. matching_orders = cursor.fetchone()
  456. # return type: (ownership_id,buy,limit,stop_loss,ordered_amount,executed_amount,expiry_dt,
  457. # ownership_id,buy,limit,stop_loss,ordered_amount,executed_amount,expiry_dt,
  458. # user_id,user_id,rowid,rowid)
  459. if not matching_orders:
  460. break
  461. buy_ownership_id, _, buy_limit, _, buy_order_amount, buy_executed_amount, buy_expiry_dt, \
  462. sell_ownership_id, _, sell_limit, _, sell_order_amount, sell_executed_amount, sell_expiry_dt, \
  463. buyer_id, seller_id, buy_order_id, sell_order_id \
  464. = matching_orders
  465. if buy_limit is None and sell_limit is None:
  466. raise AssertionError() # TODO use last transaction price
  467. elif buy_limit is None:
  468. price = sell_limit
  469. elif sell_limit is None:
  470. price = buy_limit
  471. else: # both not NULL
  472. price = (float(sell_limit) + float(buy_limit)) / 2
  473. if price == 0:
  474. raise AssertionError()
  475. buyer_money = user_money(buyer_id)
  476. amount = min(buy_order_amount - buy_executed_amount,
  477. sell_order_amount - sell_executed_amount,
  478. floor(buyer_money / price))
  479. if amount == 0: # probable because buyer has not enough money
  480. delete_order(buy_order_id)
  481. continue
  482. buy_order_finished = (buy_order_amount - buy_executed_amount - amount <= 0) or (
  483. buyer_money - amount * price < price)
  484. sell_order_finished = (sell_order_amount - sell_executed_amount - amount <= 0)
  485. if price < 0 or amount <= 0:
  486. return AssertionError()
  487. # actually execute the order, but the bank does not send or receive anything
  488. if buyer_id != bank_id(): # buyer pays
  489. cursor.execute('''
  490. UPDATE ownership
  491. SET amount = amount - ?
  492. WHERE user_id = ?
  493. AND ownable_id = ?
  494. ''', (price * amount, buyer_id, currency_id()))
  495. if seller_id != bank_id(): # seller pays
  496. cursor.execute('''
  497. UPDATE ownership
  498. SET amount = amount - ?
  499. WHERE rowid = ?
  500. ''', (amount, sell_ownership_id))
  501. if buyer_id != bank_id(): # buyer receives
  502. cursor.execute('''
  503. UPDATE ownership
  504. SET amount = amount + ?
  505. WHERE rowid = ?
  506. ''', (amount, buy_ownership_id))
  507. if seller_id != bank_id(): # seller receives
  508. cursor.execute('''
  509. UPDATE ownership
  510. SET amount = amount + ?
  511. WHERE user_id = ?
  512. AND ownable_id = ?
  513. ''', (price * amount, seller_id, currency_id()))
  514. # update order execution state
  515. cursor.execute('''
  516. UPDATE orders
  517. SET executed_amount = executed_amount + ?
  518. WHERE rowid = ?
  519. OR rowid = ?
  520. ''', (amount, buy_order_id, sell_order_id))
  521. if buy_order_finished:
  522. delete_order(buy_order_id)
  523. if sell_order_finished:
  524. delete_order(sell_order_id)
  525. if seller_id != buyer_id: # prevent showing self-transactions
  526. cursor.execute('''
  527. INSERT INTO transactions
  528. (price, ownable_id, amount)
  529. VALUES(?, ?, ?)
  530. ''', (price, ownable_id, amount,))
  531. # trigger stop loss orders
  532. if buyer_id != seller_id:
  533. cursor.execute('''
  534. UPDATE orders
  535. SET stop_loss = NULL,
  536. "limit" = NULL
  537. WHERE stop_loss IS NOT NULL
  538. AND stop_loss
  539. AND ? IN (SELECT ownable_id FROM ownership WHERE rowid = ownership_id)
  540. AND ((buy AND "limit" < ?) OR (NOT buy AND "limit" > ?))
  541. ''', (ownable_id, price, price,))
  542. def ownable_id_by_ownership_id(ownership_id):
  543. connect()
  544. cursor.execute('''
  545. SELECT ownable_id
  546. FROM ownership
  547. WHERE rowid = ?
  548. ''', (ownership_id,))
  549. return cursor.fetchone()[0]
  550. def ownable_name_by_id(ownable_id):
  551. connect()
  552. cursor.execute('''
  553. SELECT name
  554. FROM ownables
  555. WHERE rowid = ?
  556. ''', (ownable_id,))
  557. return cursor.fetchone()[0]
  558. def bank_order(buy, ownable_id, limit, amount, time_until_expiration):
  559. if not limit:
  560. raise AssertionError('The bank does not give away anything.')
  561. place_order(buy,
  562. get_ownership_id(ownable_id, bank_id()),
  563. limit,
  564. False,
  565. amount,
  566. time_until_expiration)
  567. ownable_name = ownable_name_by_id(ownable_id)
  568. cursor.execute('''
  569. INSERT INTO news(title)
  570. VALUES (?)
  571. ''', ('External investors are selling ' + ownable_name + ' atm',))
  572. def current_time(): # might differ from datetime.datetime.now() for time zone reasons
  573. connect()
  574. cursor.execute('''
  575. SELECT datetime('now')
  576. ''')
  577. return cursor.fetchone()[0]
  578. def place_order(buy, ownership_id, limit, stop_loss, amount, time_until_expiration):
  579. connect()
  580. expiry = datetime.strptime(current_time(), '%Y-%m-%d %H:%M:%S') + timedelta(minutes=time_until_expiration)
  581. cursor.execute('''
  582. INSERT INTO orders
  583. (buy, ownership_id, "limit", stop_loss, ordered_amount, expiry_dt)
  584. VALUES (?, ?, ?, ?, ?, ?)
  585. ''', (buy, ownership_id, limit, stop_loss, amount, expiry))
  586. execute_orders(ownable_id_by_ownership_id(ownership_id))
  587. return True
  588. def transactions(ownable_id):
  589. connect()
  590. cursor.execute('''
  591. SELECT dt, amount, price
  592. FROM transactions
  593. WHERE ownable_id = ?
  594. ORDER BY dt DESC
  595. ''', (ownable_id,))
  596. return cursor.fetchall()
  597. def drop_expired_orders():
  598. connect()
  599. cursor.execute('''
  600. DELETE FROM orders
  601. WHERE expiry_dt < DATETIME('now')
  602. ''')
  603. return cursor.fetchall()
  604. def generate_keys(count=1):
  605. # source https://stackoverflow.com/questions/17049308/python-3-3-serial-key-generator-list-problems
  606. for i in range(count):
  607. key = '-'.join(random_chars(5) for _ in range(5))
  608. save_key(key)
  609. print(key)
  610. def user_has_order_with_id(session_id, order_id):
  611. connect()
  612. cursor.execute('''
  613. SELECT orders.rowid
  614. FROM orders, ownership, sessions
  615. WHERE orders.rowid = ?
  616. AND sessions.session_id = ?
  617. AND sessions.user_id = ownership.user_id
  618. AND ownership.rowid = orders.ownership_id
  619. ''', (order_id, session_id,))
  620. if cursor.fetchone():
  621. return True
  622. else:
  623. return False
  624. def leaderboard():
  625. connect()
  626. cursor.execute('''
  627. SELECT *
  628. FROM ( -- one score for each user
  629. SELECT
  630. username,
  631. SUM(CASE -- sum score for each of the users ownables
  632. WHEN ownership.ownable_id = ? THEN ownership.amount
  633. ELSE ownership.amount * (SELECT price
  634. FROM transactions
  635. WHERE ownable_id = ownership.ownable_id
  636. ORDER BY dt DESC
  637. LIMIT 1)
  638. END
  639. ) score
  640. FROM users, ownership
  641. WHERE ownership.user_id = users.rowid
  642. AND users.username != 'bank'
  643. GROUP BY users.rowid
  644. ) AS scores
  645. ORDER BY score DESC
  646. LIMIT 50
  647. ''', (currency_id(),))
  648. return cursor.fetchall()
  649. def user_wealth(user_id):
  650. connect()
  651. cursor.execute('''
  652. SELECT SUM(
  653. CASE -- sum score for each of the users ownables
  654. WHEN ownership.ownable_id = ? THEN ownership.amount
  655. ELSE ownership.amount * (SELECT price
  656. FROM transactions
  657. WHERE ownable_id = ownership.ownable_id
  658. ORDER BY dt DESC
  659. LIMIT 1)
  660. END
  661. ) score
  662. FROM ownership
  663. WHERE ownership.user_id = ?
  664. ''', (currency_id(), user_id,))
  665. return cursor.fetchone()[0]
  666. def change_password(session_id, password):
  667. connect()
  668. cursor.execute('''
  669. UPDATE users
  670. SET password = ?
  671. WHERE ? IN (SELECT session_id FROM sessions WHERE sessions.user_id = users.rowid)
  672. ''', (password, session_id,))
  673. def sign_out_user(session_id):
  674. connect()
  675. cursor.execute('''
  676. DELETE FROM sessions
  677. WHERE user_id = (SELECT user_id FROM sessions s2 WHERE s2.session_id = ?)
  678. ''', (session_id,))