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)
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')
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')
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:
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)
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
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
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)
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)
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')
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')
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)
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)
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')
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')
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)
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')
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')
def drop_tables(session=None): if session is None: session = get_session() Base.metadata.drop_all(bind=session.connection(), checkfirst=True)
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)