Browse Source

ability to activate keys

Eren Yilmaz 6 years ago
parent
commit
d6d37eb508
8 changed files with 246 additions and 68 deletions
  1. 1 1
      admin_console.py
  2. 8 5
      client.py
  3. 35 4
      client_controller.py
  4. 34 27
      db_setup.py
  5. 1 1
      game.py
  6. 129 17
      model.py
  7. 5 4
      server.py
  8. 33 9
      server_controller.py

+ 1 - 1
admin_console.py

@@ -24,7 +24,7 @@ def cleanup():
 
 
 if __name__ == '__main__':
-    # generate_keys(count=8)
+    generate_keys(count=1)
     # unused_keys
 
     cleanup()

+ 8 - 5
client.py

@@ -50,16 +50,19 @@ To display an overview of available commands type \'help\'.
 ''')
 
 
-allowed_commands = ['login', 'register', 'help']
+allowed_commands = ['login', 'register', 'help', 'depot', 'activate_key']
 
 
 def one_command():
-    cmd = input('*> ')
+    cmd = input('*> ').strip()
     cmds = cmd.split(';')
     for cmd in cmds:
-        cmd = cmd.split(' ')
+        cmd = cmd.split()
+        # cmd = [cmd.strip() for cmd in cmd]
+        if cmd == []:
+            continue
         if cmd[0] not in allowed_commands:
-            print('Invalid command.')
+            print('Invalid command:', cmd[0])
         else:
             method_to_call = getattr(client_controller, cmd[0])
             try:
@@ -67,7 +70,7 @@ def one_command():
             except TypeError:
                 print('Invalid command syntax.')
             except Exception:
-                print('An error occured while executing a command.')
+                print('An error occurred while executing a command.')
 
 
 if __name__ == '__main__':

+ 35 - 4
client_controller.py

@@ -5,6 +5,9 @@ import client
 import connection
 from client import allowed_commands
 from connection import client_request
+from tabulate import tabulate
+
+from util import debug
 
 
 def login(username=None, password=None):
@@ -33,7 +36,7 @@ def login(username=None, password=None):
             print('Login failed.')
 
 
-def register(username=None, password=None, game_key=None):
+def register(username=None, password=None, game_key=''):
     if connection.session_id is not None:
         client.fake_loading_bar('Signing out', delay=0.025)
         connection.session_id = None
@@ -47,9 +50,10 @@ def register(username=None, password=None, game_key=None):
         else:
             password = input('Password: ')
 
-    if game_key is None:
-        print('Entering a game key will provide you with some starting money and other useful stuff.')
-        game_key = input('Game key (leave empty if you don\'t have one): ')
+    if not debug:
+        if game_key is '':
+            print('Entering a game key will provide you with some starting money and other useful stuff.')
+            game_key = input('Game key (leave empty if you don\'t have one): ')
 
     response = client_request('register', {"username": username, "password": password, "game_key": game_key})
 
@@ -71,3 +75,30 @@ def help():
                 print(' -', p)  # TODO print default value
         else:
             print('`' + cmd + '`', 'takes no arguments')
+
+    print('NOTE: All arguments are optional!')
+
+
+def depot():
+    response = client_request('depot', {"session_id": connection.session_id})
+    success = 'data' in response
+    if success:
+        print(tabulate(response['data'], headers=['Object', 'Amount'], tablefmt="pipe"))
+    else:
+        if 'error_message' in response:
+            print('Login failed with message:', response['error_message'])
+        else:
+            print('Login failed.')
+
+
+def activate_key(key=''):
+    if key is '':
+        print('Entering a game key may get you some money or other useful stuff.')
+        key = input('Key: ')
+
+    if key is '':
+        print('Invalid key.')
+
+    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'])

+ 34 - 27
db_setup.py

@@ -1,9 +1,7 @@
-from game import money_amount
-from model import cursor
+from game import CURRENCY_NAME
 
 
-def setup():
-
+def setup(cursor):
     print('Database setup...')
 
     replace = True
@@ -26,7 +24,7 @@ def setup():
     cursor.execute('''
                 CREATE TABLE IF NOT EXISTS ownables(
                     name VARCHAR(10) UNIQUE NOT NULL, 
-                    total_amount CURRENCY NOT NULL)
+                    total_amount CURRENCY NOT NULL DEFAULT 0)
                 ''')
     cursor.execute('''
                 CREATE TABLE IF NOT EXISTS ownership(
@@ -67,37 +65,46 @@ def setup():
     cursor.execute('''
                 CREATE TABLE IF NOT EXISTS keys(
                     key STRING UNIQUE NOT NULL,
-                    used_by_user_id INTEGER UNIQUE,
+                    used_by_user_id INTEGER,
                     FOREIGN KEY (used_by_user_id) REFERENCES user(rowid)
                 )
                 ''')
 
+    print(' - Integrity checks...')
+    cursor.execute('''
+                CREATE TRIGGER IF NOT EXISTS owned_amount_not_negative_after_insert
+                AFTER INSERT
+                ON ownership
+                WHEN NEW.amount < 0
+                BEGIN
+                    SELECT RAISE(ROLLBACK, 'Can not own an amount less than 0.');
+                END
+                ''')
+    cursor.execute('''
+                CREATE TRIGGER IF NOT EXISTS owned_amount_not_negative_after_update
+                AFTER UPDATE
+                ON ownership
+                WHEN NEW.amount < 0
+                BEGIN
+                    SELECT RAISE(ROLLBACK, 'Can not own an amount less than 0.');
+                END
+                ''')
+
     if replace:  # TODO also seed new databases
         print(' - Seeding initial data...')
-        cursor.execute('''
-                    INSERT INTO users
-                    (username, password)
-                    VALUES ("bank", "")
-                    ''')
-        cursor.execute('''
-                    SELECT rowid 
-                    FROM users
-                    WHERE username = "bank"
-                    ''')
-        bank_id = cursor.fetchone()[0]
         cursor.execute('''
                     INSERT INTO ownables
-                    (name, total_amount)
-                    VALUES ("Kollar", ?)
-                    ''', (money_amount,))
+                    (name)
+                    VALUES (?)
+                    ''', (CURRENCY_NAME,))
         cursor.execute('''
-                    SELECT rowid 
-                    FROM users
-                    WHERE username = "bank"
+                    INSERT INTO users
+                    (username,password)
+                    VALUES ('bank','')
                     ''')
-        kollar_id = cursor.fetchone()[0]
         cursor.execute('''
                     INSERT INTO ownership
-                    (user_id, ownable_id, amount)
-                    VALUES (?, ?, ?)
-                    ''', (bank_id, kollar_id, money_amount))
+                    (user_id, ownable_id)
+                    VALUES ((SELECT rowid FROM users WHERE username = 'bank'), 
+                            (SELECT rowid FROM ownables WHERE name = ?))
+                    ''', (CURRENCY_NAME,))

+ 1 - 1
game.py

@@ -1 +1 @@
-money_amount = 1000000
+CURRENCY_NAME = 'Kollar'

+ 129 - 17
model.py

@@ -4,7 +4,7 @@ import sys
 import uuid
 
 import db_setup
-from game import money_amount
+from game import CURRENCY_NAME
 from util import debug
 
 connection = None
@@ -22,6 +22,8 @@ def query_save_name():
         if re.match(r"[A-Za-z0-9.-]{0,50}", save_name):
             db_name = save_name + '.db'
             return
+        else:
+            print('Must match "[A-Za-z0-9.-]{0,50}"')
 
 
 def connect(reconnect=False):
@@ -55,7 +57,7 @@ def connect(reconnect=False):
 def setup():
     connect()
 
-    db_setup.setup()
+    db_setup.setup(cursor)
 
     connection.commit()
 
@@ -91,25 +93,53 @@ def register(username, password, game_key):
                 (username, password)
                 VALUES (? , ?)
                 ''', (username, password))
-    if game_key is not None:
-        if game_key in unused_keys():
-            cursor.execute('''
-                        UPDATE keys
-                        WHERE used_by_user_id IS NULL
-                        AND key = ?
-                        SET used_by_user_id = (
-                            SELECT rowid
-                            FROM users
-                            WHERE username = ?
-                        )
-                        ''', (game_key, username))
-        if cursor.fetchone()[0]!=1:
-            raise AssertionError()
-        # TODO: assign some money form bank
+    own(username, CURRENCY_NAME)
+    if game_key != '':
+        if valid_key(game_key):
+            activate_key(game_key, get_user_id_by_name(username))
+    return True
+
+
+def own(username, ownable_name):
+    cursor.execute('''
+                INSERT INTO ownership
+                (user_id, ownable_id)
+                VALUES ((SELECT rowid FROM users WHERE username = ?), 
+                        (SELECT rowid FROM ownables WHERE name = ?))
+                ''', (username, ownable_name))
+
+
+def send_ownable(from_user_id, to_user_id, ownable_name, amount):
+    connect()
 
+    if amount < 0:
+        return False
+
+    if from_user_id != bank_id():
+        cursor.execute('''
+                    UPDATE ownership
+                    SET amount = amount - ?
+                    WHERE user_id = ?
+                    AND ownable_id = (SELECT rowid FROM ownables WHERE name = ?)
+                    ''', (amount, from_user_id, ownable_name,))
+        if not cursor.fetchone():
+            return False
+
+    cursor.execute('''
+                UPDATE ownership
+                SET amount = amount + ?
+                WHERE user_id = ?
+                AND ownable_id = (SELECT rowid FROM ownables WHERE name = ?)
+                ''', (amount, to_user_id, ownable_name))
+    if cursor.rowcount == 0:
+        return False
     return True
 
 
+def valid_key(key):  # TODO possible performance increase when database gets larger by using sql directly
+    return key in unused_keys()
+
+
 def new_session(user_id):
     connect()
 
@@ -172,3 +202,85 @@ def unused_keys():
         ''')
 
     return [str(key[0]).strip().upper() for key in cursor.fetchall()]
+
+
+def get_user_id_by_session_id(session_id):
+    connect()
+
+    cursor.execute('''
+        SELECT users.rowid
+        FROM sessions, users
+        WHERE sessions.session_id = ?
+        AND users.rowid = sessions.user_id
+        ''', (session_id,))
+
+    ids = cursor.fetchone()
+    if ids is None:
+        return False
+    return ids[0]
+
+
+def get_user_id_by_name(username):
+    connect()
+
+    cursor.execute('''
+        SELECT users.rowid
+        FROM users
+        WHERE username = ?
+        ''', (username,))
+
+    return cursor.fetchone()[0]
+
+
+def get_user_ownership(user_id):
+    connect()
+
+    cursor.execute('''
+        SELECT ownables.name, ownership.amount
+        FROM ownership, ownables
+        WHERE user_id = ?
+        AND ownership.ownable_id = ownables.rowid
+        ''', (user_id,))
+
+    return cursor.fetchall()
+
+
+def activate_key(key, user_id):
+    connect()
+    cursor.execute('''
+                UPDATE keys
+                SET used_by_user_id = ?
+                WHERE used_by_user_id IS NULL
+                AND key = ?
+                ''', (user_id, key,))
+
+    if cursor.rowcount == 0:
+        raise AssertionError
+    send_ownable(bank_id(), user_id, CURRENCY_NAME, 1000)
+
+
+def bank_id():
+    connect()
+
+    cursor.execute('''
+        SELECT users.rowid
+        FROM users
+        WHERE username = 'bank'
+        ''')
+
+    return cursor.fetchone()[0]
+
+
+def valid_session_id(session_id):
+    connect()
+
+    cursor.execute('''
+                SELECT rowid
+                FROM sessions
+                WHERE session_id = ?
+                ''', (session_id,))
+
+    if cursor.fetchone():
+        return True
+    else:
+        return False

+ 5 - 4
server.py

@@ -13,7 +13,7 @@ if __name__ == '__main__':
 
     model.setup()
 
-    valid_routes = ['login', 'register']
+    valid_routes = ['login', 'register', 'depot', 'activate_key']
 
 
     @route('/<path>', method='POST')
@@ -26,8 +26,9 @@ if __name__ == '__main__':
             resp = method_to_call()
             model.connection.commit()
             return resp
-        except sqlite3.IntegrityError:
-            return server_controller.bad_request('action violates database constraints')
+        except sqlite3.IntegrityError as e:
+            print(e)
+            return server_controller.bad_request('Action violates database constraints.')
 
     run(host='localhost', port=connection.port, debug=debug)
-    model.db.connection.disconnect()
+    model.db.connection.disconnect()

+ 33 - 9
server_controller.py

@@ -9,15 +9,19 @@ from util import debug
 def missing_attributes(attributes):
     for attr in attributes:
         if attr not in request.json:
-            return attr
-        else:
-            return False
+            if str(attr) == 'session_id':
+                return 'You are not signed in.'
+            return 'Missing value for attribute ' + str(attr)
+        if str(attr) == 'session_id':
+            if not model.valid_session_id(request.json['session_id']):
+                return 'You are not signed in.'
+    return False
 
 
 def login():
     missing = missing_attributes(['username', 'password'])
     if missing:
-        return bad_request('Missing value for attribute ' + str(missing))
+        return bad_request(missing)
     username = request.json['username']
     password = request.json['password']
     session_id = model.login(username, password)
@@ -27,18 +31,26 @@ def login():
         return forbidden('Invalid login data')
 
 
+def depot():
+    missing = missing_attributes(['session_id'])
+    if missing:
+        return bad_request(missing)
+    data = model.get_user_ownership(model.get_user_id_by_session_id(request.json['session_id']))
+    return {'data': data}
+
+
 def register():
     missing = missing_attributes(['username', 'password'])
     if missing:
-        return bad_request('Missing value for attribute ' + str(missing))
+        return bad_request(missing)
     username = request.json['username']
     password = request.json['password']
     if model.user_exists(username):
         return forbidden('User already exists.')
-    game_key = None
+    game_key = ''
     if 'game_key' in request.json:
-        game_key = request.json['game_key']
-        if game_key not in model.unused_keys():
+        game_key = request.json['game_key'].strip().upper()
+        if game_key != '' and game_key not in model.unused_keys():
             return bad_request('Game key is not valid.')
     if model.register(username, password, game_key):
         return {'message': "successfully registered user"}
@@ -46,10 +58,22 @@ def register():
         return bad_request('registration not successful')
 
 
+def activate_key():
+    missing = missing_attributes(['key', 'session_id'])
+    if missing:
+        return bad_request(missing)
+    if model.valid_key(request.json['key']):
+        user_id = model.get_user_id_by_session_id(request.json['session_id'])
+        model.activate_key(request.json['key'], user_id)
+        return {'message': "successfully activated key"}
+    else:
+        return bad_request('Invalid key.')
+
+
 def not_found(msg=''):
     response.status = 404
     if debug:
-        msg = str(response.status) + ' Page not found: ' + msg
+        msg = str(response.status) + ': ' + msg
     response.content_type = 'application/json'
     return json.dumps({"error_message": msg})