def register_client(): """register new client with email/name + password, expected json body would have keys ["name", "email", "passcode"] """ #TODO this can through exception, need to handle it req = request.get_json(force=True) #TODO i think the required is to authenticate by email/name name = req.get('name', None) passcode = req.get('passcode', None) email = req.get('email', None) cur_pref = req.get('cur_pref', EUR) if not req or name == None or email == None or passcode == None: logger.critical("url is incomplete") print('incomplete') abort(401, 'incomplete post payload') cred_id = get_credid(email) logger.info("registering trader for client: {}".format(name)) bid = 0 db.init() lock = 2 try: db.lock_advisory(lock) email_exists = db.exists.account_byemail(email) if email_exists: print('email exists') abort(400) logger.debug("account {}/{} + {} already exists".\ format(name, email, passcode)) raise Exception("account already exists!") if not db.exists.currency(cur_pref): currency = Currency(cur_pref) if not currency.valid(): print('invalid currency') raise Exception("currency isn't supported!") db.inserts.add_currency(cur_pref, currency.rate) cur_pref_id = db.gets.get_currency_id(cur_pref) cid = db.inserts.add_client(req['name'], req['email'], cur_pref_id) logger.debug("client added") db.inserts.register(cid, passcode, cred_id) db.commit(lock) except psycopg2.DatabaseError as error: db.rollback(lock) emsg = "registering failed, error: ".format(str(error)) logger.critical(emsg) abort(500, emsg) except: db.rollback(lock) emsg = "registering failed" logger.critical(emsg) abort(403, emsg) finally: db.close() res = {'cred_id': cred_id} return jsonify(res), 201
def test_update_balance(self): exchange = Currency(EUR) rate = exchange.rate if not db.exists.currency(EUR): db.inserts.add_currency(EUR, rate) curr_id = db.gets.get_currency_id(EUR) passcode = get_rand_pass() email = get_email() credid = get_credid(email) balance = get_balance() email = get_email() name = get_name() db.inserts.add_client(name, email, curr_id) db.commit() cid = db.gets.get_client_id_byemail(email) db.inserts.register(cid, passcode, credid) db.commit() #add_bank_addount bid = db.inserts.add_bank_account(cid, balance, bank_name, branch_number, account_number, name_reference, curr_id) db.commit() db.updates.update_account(cid, 0) balance_cur = db.gets.get_balance_by_cid(cid)['balance'] self.assertEqual(balance_cur, 0)
def test_bid_cid_conversion(self): """ bid_cid conversion testing convert, and cross-reference from client id, to bank id """ exchange = Currency(EUR) rate = exchange.rate if not db.exists.currency(EUR): db.inserts.add_currency(EUR, rate) curr_id = db.gets.get_currency_id(EUR) passcode = get_rand_pass() email = get_email() credid = get_credid(email) banalce = get_balance() email = get_email() name = get_name() db.inserts.add_client(name, email, curr_id) cid = db.gets.get_client_id_byemail(email) db.inserts.register(cid, passcode, credid) #add_bank_addount bid = db.inserts.add_bank_account(cid, balance, bank_name, branch_number, account_number, name_reference, curr_id) cid_eq = db.gets.get_client_id(bid) bid_eq = db.gets.get_banking_id(cid_eq) self.assertEqual(cid_eq, cid) self.assertEqual(bid_eq, bid)
def add_bank_account(): """ register bank account for the authenticated client of the current session @param: the post body is expect to be json with keys ["bank_name", branch_number", "account_number", "name_reference"], a client can register more than one bank account for tahweela account. @return return bid (banking id), since multiple bank accounts are supported, bid need to be sent for each transaction so that, the transactions are done with it. """ req = request.get_json(force=True) bank_name = req.get("bank_name", None) branch_number = req.get("branch_number", None) account_number = req.get("account_number", None) name_reference = req.get("name_reference", "") if not req or bank_name == None or branch_number == None or account_number == None: print('-1') emsg = "incomplete request" logger.critical(emsg) abort(401, emsg) db.init() ADD_BANK_ACCOUNT_LOCK = 7 lock = ADD_BANK_ACCOUNT_LOCK try: db.lock_advisory(lock) cid = db.gets.credid2cid(session['credid']) logger.debug("client added") print('0') bank = PaymentGate(bank_name, branch_number, account_number, name_reference) if not bank.authenticated(): raise Exception('payment gate authentication failure!') print('1') balance_dt = bank.get_balance() balance = balance_dt['balance'] print('2') base_currency = balance_dt['base'] if not db.exists.currency(base_currency): currency = Currency(base_currency) db.inserts.add_currency(base_currency, currency.rate) print('3') base_currency_id = db.gets.get_currency_id(base_currency) db.inserts.add_bank_account(cid, balance, bank_name, branch_number, account_number, name_reference, base_currency_id) print('4') db.commit(lock) except psycopg2.DatabaseError as error: db.rollback(lock) emsg = "assigning bank account failed, error: " + str(error) logger.critical(emsg) abort(500, emsg) except: print('err2') db.rollback(lock) emsg = "adding bank account failed" logger.critical(emsg) abort(401) finally: db.close() return jsonify({'balance': balance, 'base': base_currency}), 201
def test_currency(self): #db.init() #db.repeatable_read() #db.lock_advisory(lock) exchange = Currency(EUR) rate = exchange.rate if not db.exists.currency(EUR): db.inserts.add_currency(EUR, rate) self.assertEqual(rate, 1)
def get_transactions_sum(self, \ trx_with_credid, \ st_dt=None, \ end_dt=None): """ get the transactions within the given period inclusively @param trx_with_credid: the credential id of the client of interest @param st_dt: the start datetime @param end_dt: the end datetime @return dataframe of the transactions """ if end_dt==None: end_dt=dt.datetime.now().strftime(TIMESTAMP_FORMAT) stat = "SELECT (ledger.trx_cost, cur.currency_name) FROM ledger INNER JOIN currency AS cur ON (cur.id=ledger.trx_cur_id) WHERE (trx_dest={to_credid} OR trx_src={from_credid});".\ format(to_credid=sql.Literal(trx_with_credid),\ from_credid=sql.Literal(trx_with_credid)) if not st_dt==None: #note! FOR UPDATE is not allowed with aggregate functions stat=sql.SQL("SELECT (ledger.trx_cost, cur.currency_name) FROM ledger INNER JOIN currency AS cur ON (cur.id=ledger.trx_cur_id) WHERE (trx_dt>={st_dt} AND trx_dt<{end_dt} AND trx_dest={to_credid}) OR (trx_dt>={st_dt} AND trx_dt<{end_dt} AND trx_src={from_credid});").\ format(st_dt=sql.Literal(st_dt),\ end_dt=sql.Literal(end_dt),\ to_credid=sql.Literal(trx_with_credid), \ from_credid=sql.Literal(trx_with_credid)) self.db_log.debug(stat) #self.cur.execute(stat) #fet=self.cur.fetchone()[0] #if fet==None: #return 0 #return fet trxs_df=pd.read_sql(stat, self.conn) #the transaction sum in euros sum=0 for i in range(len(trxs_df)): row=eval(trxs_df.iloc[i][0]) value=row[0] base=row[1] currency = Currency(EUR, base) ineuro_cost=currency.exchange(value) sum+=float(ineuro_cost) return sum
def test_client_exists(self): exchange = Currency(EUR) rate = exchange.rate if not db.exists.currency(EUR): db.inserts.add_currency(EUR, rate) curr_id = db.gets.get_currency_id(EUR) name = get_name() email = get_email() db.inserts.add_client(name, email, curr_id) cid = db.gets.get_client_id_byemail(email) self.assertTrue(db.exists.client_exists(cid))
def test_banking_byemail(self): #db.init() #db.repeatable_read() #db.lock_advisory(lock) exchange = Currency(EUR) rate = exchange.rate if not db.exists.currency(EUR): db.inserts.add_currency(EUR, rate) curr_id = db.gets.get_currency_id(EUR) db.inserts.add_client(name, email, curr_id) self.assertTrue(db.exists.account_byemail(email)) cid = db.gets.get_client_id_byemail(email) self.assertTrue(db.exists.client_exists(cid))
def get_balance(): """ get balance of the current client @return {'balance': balance, 'base': base} """ balance = None logger.info("balance requested") db.init() try: db.repeatable_read() cid = db.gets.credid2cid(session['credid']) if not db.exists.bank_account_bycid(cid): raise Exception("no bank account added yet!") #this would return balance in bank base balance = db.gets.get_balance_by_credid(session['credid']) # transform balance to user preference pref_cur = db.gets.get_preference_currency_bycid(cid) amount = balance['balance'] base = balance['base'] currency = Currency(pref_cur, base) pref_balance = currency.exchange(amount) payload = {'balance': pref_balance, 'base': pref_cur} db.commit() except psycopg2.DatabaseError as error: db.rollback() emsg = "failed request, error: {} ".format(+str(error)) logger.critical() abort(300, emsg) except: db.rollback() emsg = "failed request" logger.critical(emsg) abort(300, emsg) finally: db.close() return jsonify(payload), 201
def test_banking_byname(self): exchange = Currency(EUR) rate = exchange.rate if not db.exists.currency(EUR): db.inserts.add_currency(EUR, rate) curr_id = db.gets.get_currency_id(EUR) passcode = get_rand_pass() email = get_email() name = get_name() db.inserts.add_client(name, email, curr_id) cid = db.gets.get_client_id_byemail(email) db.inserts.register(cid, passcode, credid) self.assertTrue(db.exists.account_byname(name, passcode)) cid = db.gets.get_client_id_byname(name, passcode) self.assertTrue(db.exists.client_exists(cid))
def test_credid2cid(self): exchange = Currency(EUR) rate = exchange.rate if not db.exists.currency(EUR): db.inserts.add_currency(EUR, rate) curr_id = db.gets.get_currency_id(EUR) passcode = get_rand_pass() email = get_email() credid = get_credid(email) # add new client db.inserts.add_client(name, email, curr_id) cid = db.gets.get_client_id_byemail(email) # register client's credentials db.inserts.register(cid, passcode, credid) # credid2cid conversion cid_eq = db.gets.credid2cid(credid) self.assertEqual(cid, cid_eq) credid_eq = db.gets.cid2credid(cid) self.assertEqual(credid, credid_eq)
def test_credential_exists(self): exchange = Currency(EUR) rate = exchange.rate if not db.exists.currency(EUR): db.inserts.add_currency(EUR, rate) curr_id = db.gets.get_currency_id(EUR) name = get_name() email = get_email() passcode = get_rand_pass() credid = get_credid(email) db.inserts.add_client(name, email, curr_id) db.commit() cid = db.gets.get_client_id_byemail(email) db.commit() self.assertFalse(db.exists.credential_exists(0)) db.inserts.register(cid, passcode, credid) cid = db.gets.get_client_id_byname(name, passcode) db.commit() self.assertTrue(db.exists.credential_exists(cid))
def test_add_bank_account(self): exchange = Currency(EUR) rate = exchange.rate if not db.exists.currency(EUR): db.inserts.add_currency(EUR, rate) curr_id = db.gets.get_currency_id(EUR) passcode = get_rand_pass() email = get_email() credid = get_credid(email) banalce = get_balance() email = get_email() name = get_name() db.inserts.add_client(name, email, curr_id) cid = db.gets.get_client_id_byemail(email) db.inserts.register(cid, passcode, credid) #add_bank_addount bid = db.inserts.add_bank_account(cid, balance, bank_name, branch_number, account_number, name_reference, curr_id) self.assertTrue(db.exists.bank_account_bycid(cid))
def update_balance_preference(): """update balance preference """ req = request.get_json(force=True) base = req.get('base', None) logger.info("updating balance preference") if not req or base == None: emsg = 'incomplete url' logger.cirtical(emsg) abort(401, emsg) CURRENCY_LOCK = 11 lock = CURRENCY_LOCK db.init() try: db.lock_advisory(lock) if not db.exists.currency(base): currency = Currency(base) db.inserts.add_currency(basey, currency.rate) base_currency_id = db.gets.get_currency_id(base_currency) cid = db.gets.credid2cid(session['credid']) db.updates.currency_preference(cid, base) db.commit() except psycopg2.DatabaseError as error: db.rollback() emsg = "request failure, error: {} ".format(str(error)) logger.critical(emsg) abort(500, emsg) except Exception as error: db.rollback() emsg = "request failure, error: {}".format(str(error)) logger.critical(emsg) abort(401, emsg) finally: db.unlock_advisory(lock) db.close()
def make_transaction(): print('XXX ---------- make transaction started') req = request.get_json(force=True) recipt_credid = req['credid'] # the amount of transaction in Euro orig_amount = req['amount'] currency_base = req.get('currency', EUR) #exchange amount to euro for processing to_euro = Currency(EUR, currency_base) amount = to_euro.exchange(orig_amount) trx_name = req.get('trx_name', '') #TRANSACTION LIMITS IN EUROS max_daily = daily_limit() max_weekly = weekly_limit() #here the weekly/daily conditions are pivoted by the current moment only, note that the bank system can have specific pivot hour (the first momemnt of the day, it's specific for the bank system, and need to be known before hand) week_past = datetime.datetime.now() - datetime.timedelta(days=7) day_past = datetime.datetime.now() - datetime.timedelta(days=1) #TODO abide to the the constrains logger.info("making purchase") if not req or amount == None or recipt_credid == None: emsg = "incomplete URL empty request" logger.critical(emsg) abort(401, emsg) #gid=req['id'] db.init() MAKE_TRANSACTION_LOCK = 9 lock = MAKE_TRANSACTION_LOCK print('start transaction') try: db.lock_advisory(lock) #if this client have a bank account yet cid = db.gets.credid2cid(session['credid']) if not db.exists.bank_account_bycid(cid): raise Exception("client doesn't have any associated bank account!") #balance in bank base src_balance = db.gets.get_balance_by_credid(session['credid']) src_balance_exchange = Currency(EUR, src_balance['base']) src_balance_euro = src_balance_exchange.exchange( src_balance['balance']) if src_balance_euro < amount + FEE: emsg = "client doesn't have enough credit to make transaction" logger.critical(emsg) raise Exception(emsg) #get transaction sum in euro weekly_trx_sum = db.gets.get_transactions_sum(session['credid'], week_past) daily_trx_sum = db.gets.get_transactions_sum(session['credid'], day_past) print('got trx sum! weekly: {}, daily{}'.format( weekly_trx_sum, daily_trx_sum)) if weekly_trx_sum + amount > max_weekly or daily_trx_sum + amount > max_daily: emsg = "client passed the daily/weekly limit" logger.info(emsg) raise Exception(emsg) cur_id = db.gets.get_currency_id(currency_base) #add transaction db.inserts.insert_trx(recipt_credid, session['credid'], amount, cur_id, trx_name) #TODO this can be minimized directly by credid #dest balance in bank base print('XXX ---------- start of calculation') dest_balance = db.gets.get_balance_by_credid(recipt_credid) dest_balance_exchange = Currency(EUR, dest_balance['base']) dest_balance_euro = dest_balance_exchange.exchange( dest_balance['balance']) src_balance_new = src_balance_euro - (amount + FEE) dest_balance_new = dest_balance_euro + amount #exchange back to bank bas src_balance_new = src_balance_exchange.exchange_back(src_balance_new) dest_balance_new = dest_balance_exchange.exchange_back( dest_balance_new) src_cid = db.gets.credid2cid(session['credid']) des_cid = db.gets.credid2cid(recipt_credid) print('XXX ---------- end of calculation') if src_cid == des_cid: print("src/dest {}/{}".format(src_cid, des_cid)) emsg = "you can't make transaction with oneself!" logger.critical(emsg) raise Exception(emsg) print('XXX ---------- update balance?!') db.updates.update_account(src_cid, src_balance_new) db.updates.update_account(des_cid, dest_balance_new) trx = {'trx_dest': recipt_credid, \ 'trx_src': session['credid'], \ 'trx_cost': orig_amount, \ 'trx_name':trx_name} payload={'balance': src_balance_new, \ 'transactions': trx} print('XXX ---------- finished transaction') db.commit() except psycopg2.DatabaseError as error: db.rollback() emsg = "transaction failed, error: {}".format(str(error)) logger.critical(emsg) abort(500, emsg) except: db.rollback() emsg = "transaction failed" logger.critical(emsg) abort(401, emsg) finally: db.unlock_advisory(lock) db.close() return jsonify(payload), 201
def test_transaction(self): """ create two clients, client_1, client_2 procedure: - client_1 sends 10k to client_2 - client_1 sends 20k to client_2 - client_2 sends 5k to client_1 - transaction sum 35k sent/received """ exchange = Currency(EUR) rate = exchange.rate if not db.exists.currency(EUR): db.inserts.add_currency(EUR, rate) curr_id = db.gets.get_currency_id(EUR) c1_name = get_name() c1_email = get_email() c1_passcode = get_rand_pass() c1_credid = get_credid(c1_email) c1_bank_name = get_bank_name() c1_branch_number = get_branch_number() c1_account_number = get_account_number() c1_name_reference = get_name_reference() c1_balance = get_balance() db.inserts.add_client(c1_name, c1_email, curr_id) db.commit() c1_cid = db.gets.get_client_id_byemail(c1_email) db.inserts.register(c1_cid, c1_passcode, c1_credid) db.commit() db.inserts.add_bank_account(c1_cid, c1_balance, c1_bank_name, c1_branch_number, c1_account_number, c1_name_reference, curr_id) db.commit() # c2_name = get_name() c2_email = get_email() c2_passcode = get_rand_pass() c2_credid = get_credid(c2_email) c2_bank_name = get_bank_name() c2_branch_number = get_branch_number() c2_account_number = get_account_number() c2_name_reference = get_name_reference() c2_balance = get_balance() db.inserts.add_client(c2_name, c2_email, curr_id) db.commit() c2_cid = db.gets.get_client_id_byemail(c2_email) db.inserts.register(c2_cid, c2_passcode, c2_credid) db.commit() db.inserts.add_bank_account(c2_cid, c2_balance, c2_bank_name, c2_branch_number, c2_account_number, c2_name_reference, curr_id) db.commit() # ################ # transactions ################ costs = [10000, 20000, 5000] trx_st_0 = datetime.datetime.now().strftime(TIMESTAMP_FORMAT) db.inserts.insert_trx(c2_credid, c1_credid, costs[0], curr_id, 'transaction1') db.commit() trx_st_1 = datetime.datetime.now().strftime(TIMESTAMP_FORMAT) db.inserts.insert_trx(c2_credid, c1_credid, costs[1], curr_id, 'transaction2') db.commit() trx_st_2 = datetime.datetime.now().strftime(TIMESTAMP_FORMAT) db.inserts.insert_trx(c1_credid, c2_credid, costs[2], curr_id, 'transaction3') db.commit() trx_st_3 = datetime.datetime.now().strftime(TIMESTAMP_FORMAT) ################## # validation test ################## #epoch 1 c1_trx_sum_0 = db.gets.get_transactions_sum(c1_credid, trx_st_0) db.commit() self.assertEqual(sum(costs), c1_trx_sum_0) #epoch 2 c1_trx_sum_1 = db.gets.get_transactions_sum(c1_credid, trx_st_1) db.commit() self.assertEqual(sum(costs[1:]), c1_trx_sum_1) #epoch 3 c1_trx_sum_2 = db.gets.get_transactions_sum(c1_credid, trx_st_2) db.commit() self.assertEqual(sum(costs[2:]), c1_trx_sum_2)