Sfoglia il codice sorgente

Implement buying banking licenses

Eren Yilmaz 5 anni fa
parent
commit
c302b892ff
9 ha cambiato i file con 136 aggiunte e 46 eliminazioni
  1. 67 34
      client_controller.py
  2. 3 1
      connection.py
  3. 2 2
      db_setup/seeds/__init__.py
  4. 1 1
      db_setup/tables.py
  5. 39 2
      model.py
  6. 0 0
      notes/.keep
  7. 3 1
      routes.py
  8. 2 3
      run_client.py
  9. 19 2
      server_controller.py

+ 67 - 34
client_controller.py

@@ -5,7 +5,7 @@ from inspect import signature
 import connection
 from connection import client_request
 from debug import debug
-from game import DEFAULT_ORDER_EXPIRY
+from game import DEFAULT_ORDER_EXPIRY, CURRENCY_NAME
 from run_client import allowed_commands, fake_loading_bar
 from util import my_tabulate, yn_dialog
 
@@ -33,8 +33,8 @@ def login(username=None, password=None):
         connection.session_id = response['session_id']
         print('Login successful.')
     else:
-        if 'error_message' in response:
-            print('Login failed with message:', response['error_message'])
+        if 'error' in response:
+            print('Login failed with message:', response['error'])
         else:
             print('Login failed.')
 
@@ -76,8 +76,8 @@ def register(username=None, game_key='', password=None, retype_pw=None):
         fake_loading_bar('Validating Game Key', duration=0.4)
     response = client_request('register', {"username": username, "password": password, "game_key": game_key})
 
-    if 'error_message' in response:
-        print('Registration failed with message:', response['error_message'])
+    if 'error' in response:
+        print('Registration failed with message:', response['error'])
 
 
 def cancel_order(order_no=None):
@@ -87,8 +87,8 @@ def cancel_order(order_no=None):
     fake_loading_bar('Validating Request', duration=0.6)
     response = client_request('cancel_order', {"session_id": connection.session_id, "order_id": order_no})
 
-    if 'error_message' in response:
-        print('Order cancelling failed with message:', response['error_message'])
+    if 'error' in response:
+        print('Order cancelling failed with message:', response['error'])
 
 
 def change_pw(password=None, retype_pw=None):
@@ -117,8 +117,8 @@ def change_pw(password=None, retype_pw=None):
     fake_loading_bar('Changing password', duration=5.2)
     response = client_request('change_password', {"session_id": connection.session_id, "password": password})
 
-    if 'error_message' in response:
-        print('Changing password failed with message:', response['error_message'])
+    if 'error' in response:
+        print('Changing password failed with message:', response['error'])
 
     fake_loading_bar('Signing out', duration=0.7)
     connection.session_id = None
@@ -160,10 +160,13 @@ def depot():
         print(my_tabulate(data,
                           headers=['Object', 'Amount', 'Course', 'Bid', 'Ask', 'Est. Value'],
                           tablefmt="pipe"))
-        print('This corresponds to a wealth of roughly', response['own_wealth'])
+        wealth = response['own_wealth']
+        print(f'This corresponds to a wealth of roughly {wealth}.')
+        if response['banking_license']:
+            print(f'Also, you have a banking license.')
     else:
-        if 'error_message' in response:
-            print('Depot access failed with message:', response['error_message'])
+        if 'error' in response:
+            print('Depot access failed with message:', response['error'])
         else:
             print('Depot access failed.')
 
@@ -176,8 +179,8 @@ def leaderboard():
         print(my_tabulate(response['data'], headers=['Trader', 'Wealth'], tablefmt="pipe"))
         # print('Remember that the goal is to be as rich as possible, not to be richer than other traders!')
     else:
-        if 'error_message' in response:
-            print('Leaderboard access failed with message:', response['error_message'])
+        if 'error' in response:
+            print('Leaderboard access failed with message:', response['error'])
         else:
             print('Leaderboard access failed.')
 
@@ -192,8 +195,8 @@ def activate_key(key=''):
 
     fake_loading_bar('Validating Key', duration=0.4)
     response = client_request('activate_key', {"session_id": connection.session_id, 'key': key})
-    if 'error_message' in response:
-        print('Key activation failed with message:', response['error_message'])
+    if 'error' in response:
+        print('Key activation failed with message:', response['error'])
 
 
 def _order(is_buy_order, obj_name, amount, limit, stop_loss, expiry):
@@ -246,8 +249,8 @@ def _order(is_buy_order, obj_name, amount, limit, stop_loss, expiry):
         "stop_loss": stop_loss,
         "time_until_expiration": expiry
     })
-    if 'error_message' in response:
-        print('Order placement failed with message:', response['error_message'])
+    if 'error' in response:
+        print('Order placement failed with message:', response['error'])
     else:
         print('You might want to use the `trades` or `depot` commands',
               'to see if the order has been executed already.')
@@ -280,12 +283,42 @@ def orders():
                           headers=['Buy?', 'Name', 'Size', 'Limit', 'stop-loss', 'Expires', 'No.'],
                           tablefmt="pipe"))
     else:
-        if 'error_message' in response:
-            print('Order access failed with message:', response['error_message'])
+        if 'error' in response:
+            print('Order access failed with message:', response['error'])
         else:
             print('Order access failed.')
 
 
+def buy_banking_license():
+    fake_loading_bar('Loading data', duration=2.3)
+    fake_loading_bar('Hiring some lawyers and consultants', duration=4.6)
+    fake_loading_bar('Overcoming some bureaucratic hurdles', duration=8.95)
+    fake_loading_bar('Filling the necessary forms', duration=14.4)
+    fake_loading_bar('Waiting for bank regulation\'s response', duration=26.8)
+    response = client_request('buy_banking_license', {"session_id": connection.session_id})
+    success = 'data' in response
+    if success:
+        print('Success. You are now a bank.')
+        variables = _global_variables()
+        marginal_lending_facility = variables['marginal_lending_facility']
+        cash_reserve_free_amount = variables['cash_reserve_free_amount']
+        cash_reserve_ratio = variables['cash_reserve_ratio']
+        print(f'You are now allowed to borrow money from the central bank at the marginal '
+              f'lending facility (currently {marginal_lending_facility * 100}%).')
+        print(f'For every {CURRENCY_NAME} above {cash_reserve_free_amount} you have to '
+              f'deposit a cash reserve of {cash_reserve_ratio * 100}% at the central bank.')
+        # print('Remember that the goal is to be as rich as possible, not to be richer than other traders!')
+    else:
+        if 'error' in response:
+            print('Banking license application failed with message:', response['error'])
+        else:
+            print('Banking license application access failed.')
+
+
+def _global_variables():
+    return client_request('global_variables')
+
+
 def orders_on(obj_name=None):
     if obj_name is None:  # TODO list some available objects
         obj_name = input('Name of object to check: ')
@@ -297,8 +330,8 @@ def orders_on(obj_name=None):
                           headers=['My', 'Buy?', 'Name', 'Size', 'Limit', 'Expires', 'No.'],
                           tablefmt="pipe"))
     else:
-        if 'error_message' in response:
-            print('Order access failed with message:', response['error_message'])
+        if 'error' in response:
+            print('Order access failed with message:', response['error'])
         else:
             print('Order access failed.')
 
@@ -316,8 +349,8 @@ def gift(username=None, obj_name=None, amount=None):
                                "username": username,
                                "object_name": obj_name,
                                "amount": amount})
-    if 'error_message' in response:
-        print('Order access failed with message:', response['error_message'])
+    if 'error' in response:
+        print('Order access failed with message:', response['error'])
     elif 'message' in response:
         print(response['message'])
 
@@ -331,8 +364,8 @@ def news():
                           headers=['Date', 'Title'],
                           tablefmt="pipe"))
     else:
-        if 'error_message' in response:
-            print('News access failed with message:', response['error_message'])
+        if 'error' in response:
+            print('News access failed with message:', response['error'])
         else:
             print('News access failed.')
 
@@ -352,8 +385,8 @@ def tradables():
         print('Estimated worldwide wealth:', world_wealth)
 
     else:
-        if 'error_message' in response:
-            print('Data access failed with message:', response['error_message'])
+        if 'error' in response:
+            print('Data access failed with message:', response['error'])
         else:
             print('Data access failed.')
 
@@ -372,8 +405,8 @@ def trades_on(obj_name=None, limit=5):
                           headers=['Time', 'Volume', 'Price'],
                           tablefmt="pipe"))
     else:
-        if 'error_message' in response:
-            print('Trades access failed with message:', response['error_message'])
+        if 'error' in response:
+            print('Trades access failed with message:', response['error'])
         else:
             print('Trades access failed.')
 
@@ -389,8 +422,8 @@ def trades(limit=10):
                           headers=['Buy?', 'Name', 'Volume', 'Price', 'Time'],
                           tablefmt="pipe"))
     else:
-        if 'error_message' in response:
-            print('Trades access failed with message:', response['error_message'])
+        if 'error' in response:
+            print('Trades access failed with message:', response['error'])
         else:
             print('Trades access failed.')
 
@@ -412,8 +445,8 @@ def old_orders(include_canceled=None, include_executed=None, limit=10):
                           headers=['Buy?', 'Name', 'Size', 'Limit', 'Expiry', 'No.', 'Status'],
                           tablefmt="pipe"))
     else:
-        if 'error_message' in response:
-            print('Order access failed with message:', response['error_message'])
+        if 'error' in response:
+            print('Order access failed with message:', response['error'])
         else:
             print('Order access failed.')
 

+ 3 - 1
connection.py

@@ -104,7 +104,9 @@ def check_missing_attributes(request_json: Dict, attributes: List[str]):
                 raise Unauthorized('Invalid value for attribute ' + str(attr))
 
 
-def client_request(route, data):
+def client_request(route, data=None):
+    if data is None:
+        data = {}
     return json_request(host + '/json/' + route, data)
 
 

+ 2 - 2
db_setup/seeds/__init__.py

@@ -42,8 +42,8 @@ def seed(cursor: Cursor):
                                       AND v3.value = new_value.value))
     ''', [('banking_license_price', 5e6),
           ('personal_loan_interest_rate', 0.1),  # may seem a lot but actually this is a credit that you get without any assessment involved
-          ('deposit_facility', -0.05),  # ECB 2020
-          ('marginal_lending_facility', 0.25),  # ECB 2020
+          ('deposit_facility', -0.005),  # ECB 2020
+          ('marginal_lending_facility', 0.0025),  # ECB 2020
           ('cash_reserve_ratio', 0.01),  # Eurozone 2020
           ('cash_reserve_free_amount', 1e5),  # Eurozone 2020
           ])

+ 1 - 1
db_setup/tables.py

@@ -97,7 +97,7 @@ def tables(cursor):
     cursor.execute('''
                 CREATE TABLE IF NOT EXISTS banks(
                     rowid INTEGER PRIMARY KEY,
-                    user_id NOT NULL REFERENCES users(rowid)
+                    user_id INTEGER UNIQUE NOT NULL REFERENCES users(rowid)
                 )
                 ''')
     cursor.execute('''

+ 39 - 2
model.py

@@ -158,6 +158,7 @@ def register(username, password, game_key):
     if game_key != '':
         if valid_key(game_key):
             activate_key(game_key, get_user_id_by_name(username))
+    own(get_user_id_by_name(username), CURRENCY_NAME)
     return True
 
 
@@ -1189,8 +1190,6 @@ def cleanup():
 
 
 def ownable_ids():
-    connect()
-
     execute('''
         SELECT rowid FROM ownables
         ''')
@@ -1223,3 +1222,41 @@ def get_old_orders(user_id, include_executed, include_canceled, limit):
         ''', (user_id, include_executed, include_canceled, limit))
 
     return current_cursor.fetchall()
+
+
+def user_has_banking_license(user_id):
+    execute('''
+        SELECT EXISTS (SELECT * FROM banks WHERE user_id = ?)
+        ''', (user_id,))
+
+    return current_cursor.fetchone()[0]
+
+
+def global_control_value(value_name):
+    execute('''
+        SELECT value
+        FROM global_control_values
+        WHERE value_name = ?
+        AND dt = (SELECT MAX(dt) FROM global_control_values WHERE value_name = ?)
+        ''', (value_name, value_name,))
+
+    return current_cursor.fetchone()[0]
+
+
+def global_control_values():
+    execute('''
+        SELECT value_name, value
+        FROM global_control_values v1
+        WHERE dt IN (SELECT MAX(dt) FROM global_control_values v2 GROUP BY v2.value_name)
+        ''')
+
+    return {
+        row[0]: row[1] for row in current_cursor.fetchall()
+    }
+
+
+def assign_banking_licence(user_id):
+    execute('''
+        INSERT INTO banks(user_id)
+        VALUES (?)
+        ''', (user_id,))

+ 0 - 0
notes/.keep


+ 3 - 1
routes.py

@@ -14,7 +14,9 @@ valid_post_routes = {
     'leaderboard',
     'tradables',
     'gift',
-    'change_password'
+    'change_password',
+    'global_variables',
+    'buy_banking_license',
 }
 
 push_message_types = set()

+ 2 - 3
run_client.py

@@ -10,8 +10,6 @@ from util import yn_dialog
 
 
 def fake_loading_bar(msg, duration=5.):
-    if debug:
-        duration /= 10
     if len(msg) >= 60:
         raise AssertionError('Loading bar label too large')
     msg += ': '
@@ -74,7 +72,8 @@ allowed_commands = ['help',
                     'gift',
                     'leaderboard',
                     'activate_key',
-                    'exit']
+                    'exit',
+                    'buy_banking_license']
 
 
 def one_command():

+ 19 - 2
server_controller.py

@@ -6,7 +6,7 @@ from bottle import request
 from passlib.hash import sha256_crypt
 
 import model
-from connection import check_missing_attributes, BadRequest, Forbidden
+from connection import check_missing_attributes, BadRequest, Forbidden, PreconditionFailed
 
 
 def missing_attributes(attributes):
@@ -37,7 +37,11 @@ def depot(json_request):
     check_missing_attributes(json_request, ['session_id'])
     user_id = model.get_user_id_by_session_id(request.json['session_id'])
     return {'data': model.get_user_ownership(user_id),
-            'own_wealth': model.user_wealth(user_id)}
+            'own_wealth': model.user_wealth(user_id),
+            'banking_licence': model.user_has_banking_license(user_id)}
+
+def global_variables(_json_request):
+    return model.global_control_values()
 
 
 def register(json_request):
@@ -211,6 +215,19 @@ def change_password(json_request):
     return {'message': "Successfully changed password"}
 
 
+def buy_banking_license(json_request):
+    check_missing_attributes(json_request, ['session_id'])
+    user_id = model.get_user_id_by_session_id(json_request['session_id'])
+    if model.user_has_banking_license(user_id):
+        raise PreconditionFailed('You already have a banking license.')
+    price = model.global_control_value('banking_license_price')
+    if model.user_money(user_id) < price:
+        raise PreconditionFailed('You do not have enough money.')
+    model.send_ownable(user_id, model.bank_id(), model.currency_id(), price)
+    model.assign_banking_licence(user_id)
+    return {'message': "Successfully bought banking licencse"}
+
+
 def news(_json_request):
     return {'data': model.news()}