def post(self): """ Create a new account to associate with specified user. """ created_account_id = None schema = AccountCreationSchema() try: data = schema.loads(request.get_data()) except ValidationError as err: return self.error_response(err.messages, HTTPStatus.BAD_REQUEST) with db.session_scope() as session: account = session.query(Account).filter_by( user_id=current_user.id).first() if account: return self.error_response(ResponseErrors.ACCOUNT_EXISTS, HTTPStatus.BAD_REQUEST) data['user_id'] = current_user.id account = Account(**data) session.add(account) session.flush() created_account_id = account.id return self.success_response(result={'id': created_account_id}, status_code=HTTPStatus.CREATED)
def register(): form = RegisterForm(request.form) if form.validate_on_submit(): with db.session_scope() as session: user = session.query(User).filter_by(email=form.email.data).first() if user: return render_template( 'register.html', form=form, error='User with this email address already exists') salt = bcrypt.gensalt() hashed_pw = bcrypt.hashpw(form.password.data.encode(), salt) user_input = { 'email': form.email.data, 'password': hashed_pw, 'first_name': form.first_name.data, 'last_name': form.last_name.data, 'salt': salt } user = User(**user_input) session.add(user) session.flush() login_user(user, force=True) if current_user.is_authenticated: return redirect(url_for('account')) return redirect(url_for('auth.login')) return render_template('register.html', form=form)
def authenticate_user(email, password): with db.session_scope() as session: user = session.query(User).filter_by(email=email).first() if not user: return False password = bcrypt.hashpw(password.encode(), user.salt) if password == user.password: login_user(user) return user return False
def delete(self): """ Delete the specified brokerage account. Params ------ id : int Account identifier. """ with db.session_scope() as session: account = session.query(Account).filter_by(user_id=current_user.id) if not account: return self.error_response(ResponseErrors.ACCOUNT_DNE, HTTPStatus.NOT_FOUND) account.delete() return self.success_response(result='ok')
def get(self): """ Get account information such as cash and equity totals, as well as list of currently held stock positions. Params ------ id : int Account identifier. """ with db.session_scope() as session: account = session.query(Account).filter_by( user_id=current_user.id).first() if not account: return self.error_response(ResponseErrors.ACCOUNT_DNE, HTTPStatus.NOT_FOUND) stocks = {} for stock in account.stocks: stocks[stock.symbol] = { 'id': stock.id, 'symbol': stock.symbol, 'shares': stock.shares, 'bought_at': format(stock.bought_at, '.2f'), 'cost': format(stock.bought_at * stock.shares, '.2f') } stock_list = stocks.keys() if len(stock_list) > 0: stock_data = self.iex_api.get_stock_data(stock_list) if stock_data: for symb, stock in stock_data.items(): if symb in stocks: shares = stocks[symb]['shares'] price = float(stock['quote']['latestPrice']) stocks[symb]['price'] = "{0:.2f}".format(price) stocks[symb]['value'] = "{0:.2f}".format(price * shares) else: return self.error_response(ResponseErrors.DEFAULT) schema = AccountReadSchema() data = schema.dump(account) data['stocks'] = stocks return self.success_response(data)
def login(): form = LoginForm(request.form) if form.validate_on_submit(): with db.session_scope() as session: user = authenticate_user(form.email.data, form.password.data) if not user: return render_template('login.html', form=form, error=ResponseErrors.INVALID_LOGIN) if current_user.is_authenticated: return redirect(url_for('account')) return render_template('login.html', form=form, error=ResponseErrors.INVALID_LOGIN) return redirect(url_for('index')) return render_template('login.html', form=form)
def post(self): """ Checks database for user of given `email` and compares passwords for login verification. """ schema = UserVerifySchema() try: data = schema.loads(request.get_data()) except ValidationError as err: return self.error_response(err.messages, HTTPStatus.BAD_REQUEST) with db.session_scope() as session: user = session.query(User).filter_by(email=data['email']).first() if not user: return self.success_response(result=False) password = bcrypt.hashpw(data['password'].encode(), user.salt) verify = password == user.password return self.success_response(result=verify)
def post(self): """ Creates a new user, and creates a client associated with that user assigned to the default scope. """ schema = UserSchema() try: data = schema.loads(request.get_data()) except ValidationError as err: return self.error_response(err.messages, HTTPStatus.BAD_REQUEST) user_id = client_id = None try: with db.session_scope() as session: user = session.query(User).filter_by( email=data['email']).first() if user: return self.error_response('User already exists') salt = bcrypt.gensalt() hashed_pw = bcrypt.hashpw(data['password'].encode(), salt) user_input = { 'email': data['email'], 'password': hashed_pw, 'first_name': data['first_name'], 'last_name': data['last_name'], 'salt': salt } user = User(**user_input) session.add(user) session.flush() if not user.id: logging.debug(user_input) raise Exception('User creation failed') user_id = user.id except Exception as e: logging.debug(str(e)) return self.error_response(ResponseErrors.DEFAULT) return self.success_response(result={'user_id': user_id, 'client_id': client_id}, \ success=True, status_code=HTTPStatus.CREATED)
def get(self, id): """ Get user information based on user ID. Returns an HTTP 404 error if user doesn't exist. Params ------ id: int The identifier of the user """ with db.session_scope() as session: user = session.query(User).filter_by(id=id).first() if not user: return self.error_response(ResponseErrors.USER_DNE, HTTPStatus.NOT_FOUND) schema = UserSchema() data = schema.dump(user) if user.account: data['account_id'] = user.account.id return self.success_response(data)
def patch(self, action): """ Update the amount of available "cash" in the specified account. Params ------ id : int Account identifier. action : str 'withdraw' or 'deposit' """ if action not in ['withdraw', 'deposit']: return self.error_response(ResponseErrors.ACCOUNT_INVALID_ACTION, HTTPStatus.BAD_REQUEST) with db.session_scope() as session: account = session.query(Account).filter_by( user_id=current_user.id).first() if not account: return self.error_response(ResponseErrors.ACCOUNT_DNE, HTTPStatus.NOT_FOUND) schema = AccountUpdateSchema() try: data = schema.loads(request.get_data()) except ValidationError as err: return self.error_response(err.messages, HTTPStatus.BAD_REQUEST) if action == 'deposit': account.cash_amount += data['amount'] elif action == 'withdraw' and account.cash_amount >= data['amount']: account.cash_amount -= data['amount'] else: return self.error_response(err.messages, HTTPStatus.BAD_REQUEST) return self.success_response(result='ok')
def post(self, account_id): """ Buy a stock with given `symbol`, `shares` and `price`. Params ------ id : int Account identifier. """ result = {} schema = TradeSchema() try: data = schema.loads(request.get_data()) except ValidationError as err: return self.error_response(err.messages, HTTPStatus.BAD_REQUEST) data['account_id'] = account_id trade_type = 'buy' with db.session_scope() as session: account = session.query(Account).filter_by(id=account_id).first() if not account: return self.error_response(ResponseErrors.ACCOUNT_DNE, HTTPStatus.NOT_FOUND) if account.user_id != current_user.id: return self.error_response(ResponseErrors.ACCOUNT_NO_ACCESS, HTTPStatus.FORBIDDEN) for stock in account.stocks: if stock.symbol == data['symbol']: return self.error_response(ResponseErrors.STOCK_EXISTS, HTTPStatus.METHOD_NOT_ALLOWED) total = data['amount'] + Account.BROKERAGE_FEE if account.cash_amount < total: return self.success_response( result=ResponseErrors.NOT_ENOUGH_FUNDS, success=False) existing_s = session.query(Stock).filter_by( account_id=account_id, symbol=data['symbol']).first() if existing_s: return self.error_response(ResponseErrors.STOCK_EXISTS, HTTPStatus.BAD_REQUEST) s = Stock(account_id=account_id, bought_at=data['price'], bought_on=data['process_date'], initial_cost=data['amount'], shares=data['shares'], symbol=data['symbol']) session.add(s) session.flush() s_id = s.id if s_id: account.cash_amount -= Account.BROKERAGE_FEE account.cash_amount -= total account.equity_amount += data['amount'] result['stock_id'] = s_id ar_schema = AccountReadSchema() ar_data = ar_schema.dump(account) result['account'] = ar_data trade = Trade( user_id=account.user_id, account_id=account.id, stock_id=s_id, trade_type=trade_type, price=data['price'], shares=data['shares'], ) session.add(trade) return self.success_response(result=result, status_code=HTTPStatus.CREATED)
def put(self, account_id, stock_id): """ Trade (buy or sell) more of the specified stock. Params ------ id : int Account identifier. stock_id : int Account stock identifier. """ result = {} schema = TradeSchema() try: data = schema.loads(request.get_data()) except ValidationError as err: return self.error_response(err.messages, HTTPStatus.BAD_REQUEST) with db.session_scope() as session: account = session.query(Account).join(Account.stocks).\ filter(Account.id == account_id).first() if not account: return self.error_response(ResponseErrors.ACCOUNT_DNE, HTTPStatus.NOT_FOUND) stock = [ v for i, v in enumerate(account.stocks) if v.symbol == data['symbol'] ][0] if not stock: return self.error_response(ResponseErrors.STOCK_DNE, HTTPStatus.NOT_FOUND) if account.user_id != current_user.id: return self.error_response(ResponseErrors.ACCOUNT_NO_ACCESS, HTTPStatus.FORBIDDEN) delete_flag = False if data['trade_type'] == 'buy': cost = data['amount'] + Account.BROKERAGE_FEE if account.cash_amount < cost: return self.success_response( result=ResponseErrors.NOT_ENOUGH_FUNDS, success=False) stock.shares += data['shares'] account.cash_amount -= cost account.equity_amount += data['amount'] elif data['trade_type'] == 'sell': if account.cash_amount < Account.BROKERAGE_FEE: return self.success_response( result=ResponseErrors.NOT_ENOUGH_FUNDS, success=False) if stock.shares < data['shares']: return self.success_response( result=ResponseErrors.TOO_MANY_SHARES, success=False) if stock.shares == data['shares']: delete_flag = True stock.sold_on = data['process_date'] stock.shares -= data['shares'] account.cash_amount -= Account.BROKERAGE_FEE account.cash_amount += data['amount'] account.equity_amount -= (stock.bought_at * data['shares']) trade = Trade( user_id=account.user_id, account_id=account.id, stock_id=stock.id, trade_type=data['trade_type'], price=data['price'], shares=data['shares'], ) session.add(trade) if delete_flag: session.query(Stock).filter(Stock.id == stock.id).delete() else: session.add(stock) return self.success_response('ok')
def load_user(user_id): with db.session_scope() as session: return session.query(User).filter_by(id=user_id).first() return False