예제 #1
0
    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)
예제 #2
0
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)
예제 #3
0
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
예제 #4
0
    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')
예제 #5
0
    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)
예제 #6
0
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)
예제 #7
0
    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)
예제 #8
0
    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)
예제 #9
0
    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)
예제 #10
0
    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')
예제 #11
0
    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)
예제 #12
0
    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')
예제 #13
0
 def load_user(user_id):
     with db.session_scope() as session:
         return session.query(User).filter_by(id=user_id).first()
     return False