Browse Source

Merge pull request #10 from dancojocaru2000/Backend

Currency exchange in Backend
pull/12/head
Kenneth Bruen 3 years ago committed by GitHub
parent
commit
649e2c729f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      server/foxbank_server/apis/__init__.py
  2. 26
      server/foxbank_server/apis/forex.py
  3. 4
      server/foxbank_server/apis/notifications.py
  4. 13
      server/foxbank_server/apis/transactions.py
  5. 30
      server/foxbank_server/db_utils.py
  6. 7
      server/init.sql

2
server/foxbank_server/apis/__init__.py

@ -5,6 +5,7 @@ from .accounts import bp as acc_bp
from .login import bp as login_bp from .login import bp as login_bp
from .transactions import bp as transactions_bp from .transactions import bp as transactions_bp
from .notifications import bp as notifications_bp from .notifications import bp as notifications_bp
from .forex import bp as forex_bp
class ApiWithErr(Api): class ApiWithErr(Api):
def handle_http_exception(self, error): def handle_http_exception(self, error):
@ -31,3 +32,4 @@ def init_apis(app: Flask):
api.register_blueprint(acc_bp, url_prefix='/accounts') api.register_blueprint(acc_bp, url_prefix='/accounts')
api.register_blueprint(transactions_bp, url_prefix='/transactions') api.register_blueprint(transactions_bp, url_prefix='/transactions')
api.register_blueprint(notifications_bp, url_prefix='/notifications') api.register_blueprint(notifications_bp, url_prefix='/notifications')
api.register_blueprint(forex_bp, url_prefix='/forex')

26
server/foxbank_server/apis/forex.py

@ -0,0 +1,26 @@
from flask.views import MethodView
from flask_smorest import Blueprint
from marshmallow import Schema, fields
from ..db_utils import get_forex_rate
from .. import returns
bp = Blueprint('forex', __name__, description='Foreign Exchange information')
class GetExchangeResult(returns.SuccessSchema):
rate = fields.Float(optional=True)
@bp.get('/<from_currency>/<to_currency>')
@bp.response(422, returns.ErrorSchema, description='Invalid currency')
@bp.response(200, GetExchangeResult)
def get_exchange(from_currency: str, to_currency: str):
"""Get exchange rate between two currencies"""
if from_currency == to_currency:
rate = 1
else:
rate = get_forex_rate(from_currency, to_currency)
if rate is None:
return returns.abort(returns.invalid_argument('currency'))
return returns.success(rate=rate)

4
server/foxbank_server/apis/notifications.py

@ -1,4 +1,4 @@
from datetime import datetime from datetime import datetime, timezone
from flask.views import MethodView from flask.views import MethodView
from flask_smorest import Blueprint from flask_smorest import Blueprint
from marshmallow import Schema, fields from marshmallow import Schema, fields
@ -41,7 +41,7 @@ class NotificationsList(MethodView):
The usefulness of this endpoint is questionable besides debugging since it's a notification to self The usefulness of this endpoint is questionable besides debugging since it's a notification to self
""" """
now = datetime.now() now = datetime.now(timezone.utc).astimezone()
notification = Notification.new_notification(body, now, read) notification = Notification.new_notification(body, now, read)
insert_notification(decorators.user_id, notification) insert_notification(decorators.user_id, notification)
return returns.success(notification=notification) return returns.success(notification=notification)

13
server/foxbank_server/apis/transactions.py

@ -1,4 +1,4 @@
from datetime import date, datetime from datetime import date, datetime, timezone
from flask.views import MethodView from flask.views import MethodView
from flask_smorest import Blueprint from flask_smorest import Blueprint
from marshmallow import Schema, fields from marshmallow import Schema, fields
@ -6,7 +6,7 @@ from marshmallow import Schema, fields
import re import re
from ..decorators import ensure_logged_in from ..decorators import ensure_logged_in
from ..db_utils import get_transactions, get_account, get_accounts, insert_transaction, whose_account, insert_notification from ..db_utils import get_transactions, get_account, get_accounts, insert_transaction, whose_account, insert_notification, get_forex_rate
from ..models import Account, Notification, Transaction from ..models import Account, Notification, Transaction
from ..utils.iban import check_iban from ..utils.iban import check_iban
from .. import decorators, returns from .. import decorators, returns
@ -75,22 +75,25 @@ class TransactionsList(MethodView):
if not check_iban(destination_iban): if not check_iban(destination_iban):
return returns.abort(returns.INVALID_IBAN) return returns.abort(returns.INVALID_IBAN)
date = datetime.now() date = datetime.now(timezone.utc).astimezone()
# Check if transaction is to another FoxBank account # Check if transaction is to another FoxBank account
reverse_transaction = None reverse_transaction = None
if destination_iban[4:8] == 'FOXB': if destination_iban[4:8] == 'FOXB':
for acc in get_accounts(): for acc in get_accounts():
if destination_iban == acc.iban: if destination_iban == acc.iban:
rate = get_forex_rate(account.currency, acc.currency)
reverse_transaction = Transaction.new_transaction( reverse_transaction = Transaction.new_transaction(
date_time=date, date_time=date,
transaction_type='receive_transfer', transaction_type='receive_transfer',
status='processed', status='processed',
other_party={'iban': account.iban,}, other_party={'iban': account.iban,},
extra={ extra={
'currency': account.currency, 'currency': acc.currency,
'amount': -amount, 'amount': int(-amount * rate),
'description': description, 'description': description,
'originalAmount': -amount,
'originalCurrency': account.currency,
}, },
) )
insert_transaction(acc.id, reverse_transaction) insert_transaction(acc.id, reverse_transaction)

30
server/foxbank_server/db_utils.py

@ -306,5 +306,35 @@ class Module(ModuleType):
) )
self.db.commit() self.db.commit()
@get_db
def get_forex_rate(self, from_currency: str, to_currency: str) -> float | None:
if from_currency == to_currency:
return 1.0
cur = self.db.cursor()
if from_currency == 'RON' or to_currency == 'RON':
currency_pairs = [(from_currency, to_currency)]
else:
currency_pairs = [(from_currency, 'RON'), ('RON', to_currency)]
amount = 1.0
for currency_pair in currency_pairs:
to_select = 'to_ron'
if currency_pair[0] == 'RON':
to_select = 'from_ron'
cur.execute(
f'select {to_select} from exchange where currency = ?',
(currency_pair[1] if currency_pair[0] == 'RON' else currency_pair[0],),
)
rate = cur.fetchone()
if rate is None:
amount = None
break
rate = rate[0]
amount *= rate
return amount
sys.modules[__name__] = Module(__name__) sys.modules[__name__] = Module(__name__)

7
server/init.sql

@ -60,6 +60,13 @@ create table users_notifications (
foreign key (notification_id) references notifications (id) foreign key (notification_id) references notifications (id)
); );
create table exchange (
id integer primary key autoincrement,
currency text not null,
to_ron real not null,
from_ron real not null
);
create view V_account_balance as create view V_account_balance as
select select
accounts_transactions.account_id as "account_id", accounts_transactions.account_id as "account_id",

Loading…
Cancel
Save