model.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857
  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 debug, random_chars
  11. # connection: db.Connection = None
  12. # cursor: db.Cursor = None
  13. connection = None # no type annotations in python 3.5
  14. cursor = None
  15. db_name = None
  16. def query_save_name():
  17. global db_name
  18. if debug:
  19. db_name = 'test.db'
  20. return
  21. while True:
  22. save_name = input('Name of the savegame: ')
  23. if re.match(r"[A-Za-z0-9.-]{0,50}", save_name):
  24. db_name = save_name + '.db'
  25. return
  26. else:
  27. print('Must match "[A-Za-z0-9.-]{0,50}"')
  28. def connect(reconnect=False):
  29. global connection
  30. global cursor
  31. global db_name
  32. if reconnect:
  33. connection.commit()
  34. connection.close()
  35. cursor = None
  36. connection = None
  37. db_name = None
  38. if connection is None or cursor is None:
  39. query_save_name()
  40. try:
  41. connection = db.connect(db_name)
  42. # connection.text_factory = lambda x: unicode(x, 'utf-8', 'ignore')
  43. cursor = connection.cursor()
  44. except db.Error as e:
  45. print("Database error %s:" % e.args[0])
  46. sys.exit(1)
  47. # finally:
  48. # if con is not None:
  49. # con.close()
  50. def setup():
  51. connect()
  52. db_setup.setup(cursor)
  53. connection.commit()
  54. def used_key_count():
  55. connect()
  56. cursor.execute('''
  57. SELECT COUNT(*)
  58. FROM keys
  59. WHERE used_by_user_id IS NOT NULL
  60. ''')
  61. return cursor.fetchone()[0]
  62. def login(username, password):
  63. connect()
  64. # do not allow login as bank
  65. if password == '':
  66. return None
  67. cursor.execute('''
  68. SELECT rowid
  69. FROM users
  70. WHERE username = ?
  71. AND password = ?
  72. ''', (username, password))
  73. user_id = cursor.fetchone()
  74. if user_id:
  75. return new_session(user_id)
  76. else:
  77. return None
  78. def register(username, password, game_key):
  79. connect()
  80. if username == '':
  81. return False
  82. if password == '':
  83. return False
  84. cursor.execute('''
  85. INSERT INTO users
  86. (username, password)
  87. VALUES (? , ?)
  88. ''', (username, password))
  89. own(get_user_id_by_name(username), CURRENCY_NAME)
  90. if game_key != '':
  91. if valid_key(game_key):
  92. activate_key(game_key, get_user_id_by_name(username))
  93. return True
  94. def own(user_id, ownable_name):
  95. if not isinstance(ownable_name, str):
  96. return AssertionError('A name must be a string.')
  97. cursor.execute('''
  98. WITH one_ownable_id AS (SELECT rowid FROM ownables WHERE name = ?),
  99. one_user_id AS (SELECT ?)
  100. INSERT INTO ownership (user_id, ownable_id)
  101. SELECT *
  102. FROM one_user_id, one_ownable_id
  103. WHERE NOT EXISTS (
  104. SELECT * FROM ownership
  105. WHERE ownership.user_id IN one_user_id
  106. AND ownership.ownable_id IN one_ownable_id
  107. )
  108. ''', (ownable_name, user_id,))
  109. def send_ownable(from_user_id, to_user_id, ownable_name, amount):
  110. connect()
  111. if amount < 0:
  112. return False
  113. if from_user_id != bank_id():
  114. cursor.execute('''
  115. UPDATE ownership
  116. SET amount = amount - ?
  117. WHERE user_id = ?
  118. AND ownable_id = (SELECT rowid FROM ownables WHERE name = ?)
  119. ''', (amount, from_user_id, ownable_name,))
  120. cursor.execute('''
  121. UPDATE ownership
  122. SET amount = amount + ?
  123. WHERE user_id = ?
  124. AND ownable_id = (SELECT rowid FROM ownables WHERE name = ?)
  125. ''', (amount, to_user_id, ownable_name))
  126. return True
  127. def valid_key(key):
  128. connect()
  129. cursor.execute('''
  130. SELECT key
  131. FROM keys
  132. WHERE used_by_user_id IS NULL
  133. AND key = ?
  134. ''', (key,))
  135. if cursor.fetchone():
  136. return True
  137. else:
  138. return False
  139. def new_session(user_id):
  140. connect()
  141. session_id = str(uuid.uuid4())
  142. cursor.execute('''
  143. INSERT INTO SESSIONS
  144. (user_id, session_id)
  145. VALUES (? , ?)
  146. ''', (user_id[0], session_id))
  147. return session_id
  148. def save_key(key):
  149. connect()
  150. cursor.execute('''
  151. INSERT INTO keys
  152. (key)
  153. VALUES (?)
  154. ''', (key,))
  155. def drop_old_sessions():
  156. connect()
  157. cursor.execute('''
  158. DELETE FROM sessions s1
  159. WHERE
  160. (SELECT COUNT(*) as newer
  161. FROM sessions s2
  162. WHERE s1.user_id = s2.user_id
  163. AND s1.rowid < s2.rowid) >= 10
  164. ''')
  165. def user_exists(username):
  166. connect()
  167. cursor.execute('''
  168. SELECT rowid
  169. FROM users
  170. WHERE username = ?
  171. ''', (username,))
  172. if cursor.fetchone():
  173. return True
  174. else:
  175. return False
  176. def unused_keys():
  177. connect()
  178. cursor.execute('''
  179. SELECT key
  180. FROM keys
  181. WHERE used_by_user_id IS NULL
  182. ''')
  183. return [str(key[0]).strip().upper() for key in cursor.fetchall()]
  184. def get_user_id_by_session_id(session_id):
  185. connect()
  186. cursor.execute('''
  187. SELECT users.rowid
  188. FROM sessions, users
  189. WHERE sessions.session_id = ?
  190. AND users.rowid = sessions.user_id
  191. ''', (session_id,))
  192. ids = cursor.fetchone()
  193. if not ids:
  194. return False
  195. return ids[0]
  196. def get_user_id_by_name(username):
  197. connect()
  198. cursor.execute('''
  199. SELECT users.rowid
  200. FROM users
  201. WHERE username = ?
  202. ''', (username,))
  203. return cursor.fetchone()[0]
  204. def get_user_ownership(user_id):
  205. connect()
  206. cursor.execute('''
  207. SELECT
  208. ownables.name,
  209. ownership.amount,
  210. COALESCE (
  211. CASE -- sum score for each of the users ownables
  212. WHEN ownership.ownable_id = ? THEN 1
  213. ELSE (SELECT price
  214. FROM transactions
  215. WHERE ownable_id = ownership.ownable_id
  216. ORDER BY dt DESC
  217. LIMIT 1)
  218. END, 0) AS value
  219. FROM ownership, ownables
  220. WHERE user_id = ?
  221. AND ownership.amount > 0
  222. AND ownership.ownable_id = ownables.rowid
  223. ''', (currency_id(), user_id,))
  224. return cursor.fetchall()
  225. def activate_key(key, user_id):
  226. connect()
  227. cursor.execute('''
  228. UPDATE keys
  229. SET used_by_user_id = ?
  230. WHERE used_by_user_id IS NULL
  231. AND key = ?
  232. ''', (user_id, key,))
  233. send_ownable(bank_id(), user_id, CURRENCY_NAME, 1000)
  234. def bank_id():
  235. connect()
  236. cursor.execute('''
  237. SELECT users.rowid
  238. FROM users
  239. WHERE username = 'bank'
  240. ''')
  241. return cursor.fetchone()[0]
  242. def valid_session_id(session_id):
  243. connect()
  244. cursor.execute('''
  245. SELECT rowid
  246. FROM sessions
  247. WHERE session_id = ?
  248. ''', (session_id,))
  249. if cursor.fetchone():
  250. return True
  251. else:
  252. return False
  253. def get_user_orders(user_id):
  254. connect()
  255. cursor.execute('''
  256. SELECT
  257. CASE
  258. WHEN orders.buy THEN 'Buy'
  259. ELSE 'Sell'
  260. END,
  261. ownables.name,
  262. orders.ordered_amount - orders.executed_amount,
  263. orders."limit",
  264. CASE
  265. WHEN orders."limit" IS NULL THEN NULL
  266. WHEN orders.stop_loss THEN 'Yes'
  267. ELSE 'No'
  268. END,
  269. orders.ordered_amount,
  270. datetime(orders.expiry_dt),
  271. orders.rowid
  272. FROM orders, ownables, ownership
  273. WHERE ownership.user_id = ?
  274. AND ownership.ownable_id = ownables.rowid
  275. AND orders.ownership_id = ownership.rowid
  276. ORDER BY ownables.name ASC, orders.stop_loss ASC, orders.buy DESC, orders."limit" ASC
  277. ''', (user_id,))
  278. return cursor.fetchall()
  279. def get_ownable_orders(ownable_id):
  280. connect()
  281. cursor.execute('''
  282. SELECT
  283. CASE
  284. WHEN orders.buy THEN 'Buy'
  285. ELSE 'Sell'
  286. END,
  287. ownables.name,
  288. orders.ordered_amount - orders.executed_amount,
  289. orders."limit",
  290. CASE
  291. WHEN orders."limit" IS NULL THEN NULL
  292. WHEN orders.stop_loss THEN 'Yes'
  293. ELSE 'No'
  294. END,
  295. datetime(orders.expiry_dt),
  296. orders.rowid
  297. FROM orders, ownables, ownership
  298. WHERE ownership.ownable_id = ?
  299. AND ownership.ownable_id = ownables.rowid
  300. AND orders.ownership_id = ownership.rowid
  301. ORDER BY ownables.name ASC, orders.stop_loss ASC, orders.buy DESC, orders."limit" ASC
  302. ''', (ownable_id,))
  303. return cursor.fetchall()
  304. def sell_ordered_amount(user_id, ownable_id):
  305. connect()
  306. # if ownable_id == currency_id():
  307. # return 0
  308. cursor.execute('''
  309. SELECT COALESCE(SUM(orders.ordered_amount - orders.executed_amount),0)
  310. FROM orders, ownership
  311. WHERE ownership.rowid = orders.ownership_id
  312. AND ownership.user_id = ?
  313. AND ownership.ownable_id = ?
  314. AND NOT orders.buy
  315. ''', (user_id, ownable_id))
  316. return cursor.fetchone()[0]
  317. def user_owns_at_least(amount, user_id, ownable_id):
  318. connect()
  319. if not isinstance(amount, float) and not isinstance(amount, int):
  320. # comparison of float with strings does not work so well in sql
  321. raise AssertionError()
  322. cursor.execute('''
  323. SELECT rowid
  324. FROM ownership
  325. WHERE user_id = ?
  326. AND ownable_id = ?
  327. AND amount - ? >= ?
  328. ''', (user_id, ownable_id, sell_ordered_amount(user_id, ownable_id), amount))
  329. if cursor.fetchone():
  330. return True
  331. else:
  332. return False
  333. def news():
  334. connect()
  335. cursor.execute('''
  336. SELECT *
  337. FROM news
  338. ORDER BY dt DESC
  339. LIMIT 20
  340. ''')
  341. return cursor.fetchall()
  342. def ownable_name_exists(name):
  343. connect()
  344. cursor.execute('''
  345. SELECT rowid
  346. FROM ownables
  347. WHERE name = ?
  348. ''', (name,))
  349. if cursor.fetchone():
  350. return True
  351. else:
  352. return False
  353. def new_stock(timeout=60, name=None):
  354. connect()
  355. while name is None:
  356. name = random_chars(6)
  357. if ownable_name_exists(name):
  358. name = None
  359. cursor.execute('''
  360. INSERT INTO ownables(name)
  361. VALUES (?)
  362. ''', (name,))
  363. cursor.execute('''
  364. INSERT INTO news(title)
  365. VALUES (?)
  366. ''', ('A new stock can now be bought: ' + name,))
  367. if random.getrandbits(1):
  368. cursor.execute('''
  369. INSERT INTO news(title)
  370. VALUES (?)
  371. ''', ('Experts expect the price of ' + name + ' to fall',))
  372. else:
  373. cursor.execute('''
  374. INSERT INTO news(title)
  375. VALUES (?)
  376. ''', ('Experts expect the price of ' + name + ' to rise',))
  377. amount = random.randrange(100, 10000)
  378. price = random.randrange(10000, 20000) / amount
  379. ownable_id = ownable_id_by_name(name)
  380. own(bank_id(), name)
  381. bank_order(False,
  382. ownable_id,
  383. price,
  384. amount,
  385. timeout)
  386. return name
  387. def new_stocks(timeout=60, count=1):
  388. return [new_stock(timeout=timeout) for _ in range(count)]
  389. def ownable_id_by_name(ownable_name):
  390. connect()
  391. cursor.execute('''
  392. SELECT rowid
  393. FROM ownables
  394. WHERE name = ?
  395. ''', (ownable_name,))
  396. return cursor.fetchone()[0]
  397. def get_ownership_id(ownable_id, user_id):
  398. connect()
  399. cursor.execute('''
  400. SELECT rowid
  401. FROM ownership
  402. WHERE ownable_id = ?
  403. AND user_id = ?
  404. ''', (ownable_id, user_id,))
  405. return cursor.fetchone()[0]
  406. def currency_id():
  407. connect()
  408. cursor.execute('''
  409. SELECT rowid
  410. FROM ownables
  411. WHERE name = ?
  412. ''', (CURRENCY_NAME,))
  413. return cursor.fetchone()[0]
  414. def user_money(user_id):
  415. connect()
  416. cursor.execute('''
  417. SELECT amount
  418. FROM ownership
  419. WHERE user_id = ?
  420. AND ownable_id = ?
  421. ''', (user_id, currency_id()))
  422. return cursor.fetchone()[0]
  423. def delete_order(order_id):
  424. connect()
  425. cursor.execute('''
  426. DELETE FROM orders
  427. WHERE rowid = ?
  428. ''', (order_id,))
  429. def execute_orders(ownable_id):
  430. connect()
  431. while True:
  432. # find order to execute
  433. cursor.execute('''
  434. SELECT buy_order.*, sell_order.*, buyer.user_id, seller.user_id, buy_order.rowid, sell_order.rowid
  435. FROM orders buy_order, orders sell_order, ownership buyer, ownership seller
  436. WHERE buy_order.buy AND NOT sell_order.buy
  437. AND buyer.rowid = buy_order.ownership_id
  438. AND seller.rowid = sell_order.ownership_id
  439. AND buyer.ownable_id = ?
  440. AND seller.ownable_id = ?
  441. AND (buy_order."limit" IS NULL
  442. OR sell_order."limit" IS NULL
  443. OR (sell_order."limit" <= buy_order."limit"
  444. AND NOT sell_order.stop_loss
  445. AND NOT buy_order.stop_loss))
  446. ORDER BY CASE WHEN sell_order."limit" IS NULL THEN 0 ELSE 1 END ASC,
  447. CASE WHEN buy_order."limit" IS NULL THEN 0 ELSE 1 END ASC,
  448. buy_order."limit" DESC,
  449. sell_order."limit" ASC,
  450. buy_order.ordered_amount - buy_order.executed_amount DESC,
  451. sell_order.ordered_amount - sell_order.executed_amount DESC
  452. LIMIT 1
  453. ''', (ownable_id, ownable_id,))
  454. matching_orders = cursor.fetchone()
  455. # return type: (ownership_id,buy,limit,stop_loss,ordered_amount,executed_amount,expiry_dt,
  456. # ownership_id,buy,limit,stop_loss,ordered_amount,executed_amount,expiry_dt,
  457. # user_id,user_id,rowid,rowid)
  458. if not matching_orders:
  459. break
  460. # TODO continue and delete order if buyer has not enough money
  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 find a solution
  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. ''', (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]