Esempio n. 1
0
    def run(self, username, stock_symbol, amount):
        self.session = get_session()
        user = self.session.query(User).filter_by(username=username).first()
        if not user:
            raise UserNotFoundError(username)
            xml.log_error('BUY', 'username not found')
        # Check for existing uncommitted transaction
        if self.session.query(Transaction).filter_by(
                user=user, operation='BUY', committed=False).count() > 0:
            raise BuyTransactionActiveError()
            xml.log_error('BUY', 'Outstanding Buy Exists')

        # Getting stock quote
        quote_client = get_quote_client()
        quote = quote_client.get_quote(stock_symbol, username)

        # Work out quantity of stock to buy, fail if not enough for one stock
        amount = Money.from_string(amount)
        quantity = amount_to_quantity(quote, amount)
        if quantity == 0:
            raise InsufficientFundsError()

        price = quote * quantity
        if user.account_balance < price:
            raise InsufficientFundsError()

        transaction = Transaction(user=user, quantity=quantity, operation='BUY', 
                stock_symbol=stock_symbol, stock_value=quote, committed=False)

        self.session.add(transaction)
        self.session.commit()

        xml.log_transaction('BUY', transaction) 
        return xml.QuoteResponse(quantity=quantity, price=price)
Esempio n. 2
0
    def run(self, username, stock_symbol, quantity):
        self.session = get_session()
        user = self.session.query(User).filter_by(username=username).first()
        if not user:
            raise UserNotFoundError(username)

        quantity = int(quantity)

        # see if the user owns the requested stock and has enough for request
        records = self.session.query(StockPurchase).filter_by(
                username=user.username, stock_symbol=stock_symbol).all()

        if(len(records) > 1):
            raise UnknownCommandError('Multiple StockPurchase for user %s: %d', 
                    username, len(records))
        if len(records) == 0 or records[0].quantity < quantity:
            raise InsufficientStockError()

        # Create inactive Trigger
        set_transaction = Trigger(user=user, quantity=quantity,
                operation='SELL', stock_symbol=stock_symbol, state=Trigger.State.INACTIVE)
        self.session.add(set_transaction)

        self.session.commit()

        xml.log_trigger('SET_SELL_AMOUNT', set_transaction, status_message='success')

        return xml.ResultResponse('success')
Esempio n. 3
0
    def run(self, username):
        self.session = get_session()
        user = self.session.query(User).filter_by(username=username).first()
        if not user:
            raise UserNotFoundError(username)
        transaction = self.session.query(Transaction).filter_by(
            username=user.username, operation='SELL', committed=False
        ).first()
        if not transaction:
            raise NoSellTransactionError(username)

        if (datetime.now() - transaction.creation_time) > config.TRANSACTION_TIMEOUT:
            self.session.delete(transaction)
            self.session.commit()
            raise ExpiredSellTransactionError(username)

        price = transaction.stock_value * transaction.quantity

        user.account_balance += price

        # update the StockPurchase for this stock symbol
        stock = self.session.query(StockPurchase).filter_by(
            user=user, stock_symbol=transaction.stock_symbol
        ).one()
        stock.quantity = stock.quantity - transaction.quantity

        transaction.committed = True
        self.session.commit()


        xml.log_transaction('COMMIT_SELL', transaction, status_message='success')
        return xml.ResultResponse('success')
Esempio n. 4
0
    def check_trigger(self, trigger_id):
        session = None

        while True:
            try:
                session = get_session()

                # Refresh trigger from new session
                trigger = session.query(Trigger).filter_by(id=trigger_id).first()

                log.debug('Trigger %d checking for stock %s > %s',
                        trigger.id, trigger.stock_symbol, trigger.trigger_value)

                if trigger.state == Trigger.State.CANCELLED:
                    log.debug('Trigger %d cancelled!', trigger.id)
                    return

                # Get a new quote for the stock
                quote_client = get_quote_client()
                quote = quote_client.get_quote(trigger.stock_symbol, 
                        trigger.username)
                log.debug('Trigger %d: %s => %s', 
                        trigger.id, trigger.stock_symbol, quote)

                # If quote is greater than trigger value, buy stock and remove trigger
                if quote > trigger.trigger_value:
                    # buy the stock and update reserve balance
                    log.debug("Trigger %d activated: %s > %s", 
                            trigger.id, quote, trigger.trigger_value)
                    return self.process_transaction(session, quote, trigger)

            except Exception, e:
                log.error('Trigger %d: %s : %s', trigger_id, type(e), e)
            finally:
Esempio n. 5
0
def drop_tables():
    """ LOCAL: Drops all database tables. Will fail if tables don't exist. """
    from sps.database.session import get_session
    from sps.database.models import Base
    session = get_session()

    # Drop all tables except user
    tables = Base.metadata.tables
    non_user_tables = [table for tablename, table in tables.items() if tablename != 'users']
    Base.metadata.drop_all(bind=session.connection(), checkfirst=False,
        tables=non_user_tables)

    # Now drop the user table
    Base.metadata.drop_all(bind=session.connection(), checkfirst=False)
Esempio n. 6
0
def sell_transaction_and_user(session=None):
    """
    Inserts a user and associated SELL transaction into the test database
    """
    if session is None:
        session = get_session()

    user = User(username='******', password='******',
        account_balance=Money(100, 50), reserve_balance=Money(0, 0))
    trans = Transaction(user=user, stock_symbol='AAAA',
        operation='SELL', committed=False, quantity=2,
        stock_value=Money(10, 40))
    session.add_all((user, trans))
    session.commit()
    return user, trans
Esempio n. 7
0
def users(session=None):
    """ Inserts user rows into the test database """
    if session is None:
        session = get_session()

    users = [
        User(username='******', password='******',
            account_balance=Money(0, 0), reserve_balance=Money(0, 0)),
        User(username='******', password='******'),
        User(username='******', password='******',
            account_balance=Money(1000, 50), reserve_balance=Money(0, 0)),
    ]
    session.add_all(users)
    session.commit()
    return users
Esempio n. 8
0
    def dumplog_admin(self, filename):
        # TODO: implement security for admin dumplog

        self.session = get_session()
        transactions = self.session.query(Transaction).all()

        # Note: this is not secure against directory traversal
        full_path = path.join(config.DUMPLOG_DIR, filename)
        with open(full_path, 'w') as f:
            res = xml.DumplogResponse(transactions)
            f.write(str(res))


        xml.log_event('DUMPLOG', username='******', status_message='to file %s' % filename)
        return xml.ResultResponse('Wrote transactions to "%s"' % full_path)
Esempio n. 9
0
    def run(self, username, stock_symbol, money_amount):

        # see if user exists
        self.session = get_session()
        user = self.session.query(User).filter_by(username=username).first()
        if not user:
            raise UserNotFoundError(username)

        # Check for existing uncommitted transaction
        if self.session.query(Transaction).filter_by(
                user=user, operation='SELL', committed=False).count() > 0:
            raise SellTransactionActiveError()

        #set up client to get quote
        quote_client = get_quote_client()
        quoted_stock_value = quote_client.get_quote(stock_symbol, username) 

        # Work out quantity of stock to sell, fail if not enough for one stock
        money_amount = Money.from_string(money_amount)
        quantity_to_sell = amount_to_quantity(quoted_stock_value, money_amount)
        if quantity_to_sell == 0:
            raise InsufficientFundsError()


        # see if the user owns the requested stock and has enough for request
        records = self.session.query(StockPurchase).filter_by(
                username=user.username, stock_symbol=stock_symbol).all()

        if(len(records) > 1):
            raise UnknownCommandError('Multiple StockPurchase for user %s: %d', 
                    username, len(records))
        if len(records) != 1 or records[0].quantity < quantity_to_sell:
            raise InsufficientStockError()

        price = quoted_stock_value * quantity_to_sell

        # make transaction
        self.trans = Transaction(username=user.username, 
                stock_symbol=stock_symbol, operation='SELL', committed=False, 
                quantity=quantity_to_sell, stock_value=quoted_stock_value)

        # commit transaction after all actions for atomicity
        self.session.add(self.trans)
        self.session.commit()


        xml.log_transaction('SELL', self.trans, status_message='success')
        return xml.QuoteResponse(quantity=quantity_to_sell, price=price)
Esempio n. 10
0
    def run(self, username, amount):
        self.session = get_session()
        user = self.session.query(User).filter_by(username=username).first()
        if not user:
            log.info('User does not exist, creating one first...')
            user = User(username=username, password='',
                account_balance=Money(0, 0), reserve_balance=Money(0, 0))
            self.session.add(user)

        amount = Money.from_string(amount)
        user.account_balance += amount
        self.session.commit()

        #log event
        xml.log_event('ADD', username, amount=str(amount)) 

        return xml.ResultResponse('success')
Esempio n. 11
0
    def run(self, username):
        self.session = get_session()
        user = self.session.query(User).filter_by(username=username).first()
        if not user:
            xml.log_error('CANCEL_BUY', 'username not found')
            raise UserNotFoundError(username)
        transaction = self.session.query(Transaction).filter_by(
            username=user.username, operation='BUY', committed=False
        ).first()
        if not transaction:
            raise NoBuyTransactionError(username)

        self.session.delete(transaction)
        self.session.commit()

        xml.log_transaction('CANCEL_BUY', transaction, status_message='success')
        return xml.ResultResponse('success')
Esempio n. 12
0
    def dumplog_user(self, username, filename):
        self.session = get_session()
        user = self.session.query(User).filter_by(username=username).first()
        if not user:
            raise UserNotFoundError(username)

        # Get this users transactions
        transactions = self.session.query(Transaction).filter_by(user=user).all()

        # Note: this is not secure against directory traversal
        full_path = path.join(config.DUMPLOG_DIR, filename)
        with open(full_path, 'w') as f:
            res = xml.DumplogResponse(transactions)
            f.write(str(res))


        xml.log_event('DUMPLOG', username, status_message='to file %s' % filename)
        return xml.ResultResponse('Wrote transactions to "%s"' % full_path)
Esempio n. 13
0
    def run(self, username):
        self.session = get_session()
        user = self.session.query(User).filter_by(username=username).first()
        if not user:
            raise UserNotFoundError(username)

        # Get this users transactions
        transactions = self.session.query(Transaction).filter_by(user=user).all()

        # Get this users triggers
        triggers = self.session.query(Trigger).filter_by(user=user).all()

        # Get this users stocks
        stocks = self.session.query(StockPurchase).filter_by(user=user).all()

        xml.log_event('DISPLAY_SUMMARY', username)
        return xml.SummaryResponse(
            transactions=transactions, triggers=triggers, stocks=stocks,
            account_balance=user.account_balance,
            reserve_balance=user.reserve_balance)
Esempio n. 14
0
    def run(self, username, stock_symbol):
        self.session = get_session()
        user = self.session.query(User).filter_by(username=username).first()
        if not user:
            raise UserNotFoundError(username)

        trigger = self.session.query(Trigger).filter_by(
            username=user.username, operation='SELL', stock_symbol=stock_symbol,
        ).filter(
            Trigger.state != Trigger.State.CANCELLED,
        ).first()

        if not trigger:
            raise NoTriggerError(username, stock_symbol)

        trigger.state = Trigger.State.CANCELLED
        self.session.commit()

        xml.log_trigger('CANCEL_SET_SELL', trigger, status_message='success')
        return xml.ResultResponse('trigger cancelled')
Esempio n. 15
0
    def run(self, username):
        self.session = get_session()
        user = self.session.query(User).filter_by(username=username).first()
        if not user:
            xml.log_error('COMMIT_BUY', 'username not found')
            raise UserNotFoundError(username)
        transaction = self.session.query(Transaction).filter_by(
            username=user.username, operation='BUY', committed=False
        ).first()
        if not transaction:
            xml.log_error('COMMIT_BUY', 'no buy tranaction found')
            raise NoBuyTransactionError(username)

        if (datetime.now() - transaction.creation_time) > config.TRANSACTION_TIMEOUT:
            self.session.delete(transaction)
            self.session.commit()
            raise ExpiredBuyTransactionError(username)

        price = transaction.stock_value * transaction.quantity

        user.account_balance -= price
        transaction.committed = True

        # create or update the StockPurchase for this stock symbol
        stock = self.session.query(StockPurchase).filter_by(
            user=user, stock_symbol=transaction.stock_symbol
        ).first()
        if not stock:
            stock = StockPurchase(user=user,
                    stock_symbol=transaction.stock_symbol,
                    quantity=transaction.quantity)
        else:
            stock.quantity = stock.quantity + transaction.quantity

        self.session.commit()
        xml.log_transaction('COMMIT_BUY', transaction, status_message='success')

        return xml.ResultResponse('success')
Esempio n. 16
0
    def setUp(self):
        # Re-use the same test database for all tests using this class
        if DatabaseTest._TEST_ENGINE is None:

            if config.TEST_WITH_SQLITE:
                # Create an in-memory sqlite database for fast testing
                DatabaseTest._TEST_ENGINE = create_engine('sqlite:///:memory:')
            else:
                # Test with real MySQL database for completeness (much slower)
                _URL = URL(**config.DATABASE_CONNECTION_ARGS)

                DatabaseTest._TEST_ENGINE = create_engine(
                    _URL,
                    **config.DATABASE_ENGINE_ARGS
                )

        setup_database(engine=self._TEST_ENGINE)
        self.session = get_session()

        # Re-create the tables before each test
        Base.metadata.drop_all(bind=self.session.connection(),
                checkfirst=True)
        Base.metadata.create_all(bind=self.session.connection(),
                checkfirst=False)
Esempio n. 17
0
    def run(self, username, stock_symbol, amount):
        self.session = get_session()
        user = self.session.query(User).filter_by(username=username).first()
        if not user:
            raise UserNotFoundError(username)

        amount = Money.from_string(amount)

        trigger = self.session.query(Trigger).filter_by(
            username=user.username, operation='SELL', stock_symbol=stock_symbol,
            state=Trigger.State.INACTIVE
        ).first()
        if not trigger:
            raise NoTriggerError(username, stock_symbol)

        trigger.state = Trigger.State.RUNNING
        trigger.trigger_value = amount
        self.session.commit()

        eventlet.spawn(self.check_trigger, trigger.id)

        xml.log_trigger('SET_SELL_TRIGGER', trigger, status_message='trigger set')
        self.session.close()
        return xml.ResultResponse('trigger activated')
Esempio n. 18
0
    def run(self, username, stock_symbol, amount):
        self.session = get_session()
        user = self.session.query(User).filter_by(username=username).first()
        if not user:
            raise UserNotFoundError(username)

        # Work out quantity of stock to buy, fail if user has insufficient funds
        amount = Money.from_string(amount)
        if user.account_balance < amount:
            raise InsufficientFundsError()

        # Create inactive Trigger
        set_transaction = Trigger(user=user, amount=amount,
                operation='BUY', stock_symbol=stock_symbol, state=Trigger.State.INACTIVE)
        self.session.add(set_transaction)

        user.account_balance = user.account_balance - amount
        user.reserve_balance += amount

        self.session.commit()

        xml.log_trigger('SET_BUY_AMOUNT', set_transaction, status_message='success')

        return xml.ResultResponse('success')
Esempio n. 19
0
def drop_tables(session=None):
    if session is None:
        session = get_session()
    Base.metadata.drop_all(bind=session.connection(), checkfirst=True)
Esempio n. 20
0
def create_tables():
    """ LOCAL: Creates all database tables. Will fail if tables already exist. """
    from sps.database.session import get_session
    from sps.database.models import Base
    session = get_session()
    Base.metadata.create_all(bind=session.connection(), checkfirst=False)