diff --git a/BotErrors.py b/BotErrors.py index dd9bace..730b226 100644 --- a/BotErrors.py +++ b/BotErrors.py @@ -21,7 +21,7 @@ class UsernameLookupFailed(Exception): class PlayerNotFound(DataBaseError): '''Player not found in database.''' -class LocationNameNotUniqueError(DataBaseError): +class EntryNameNotUniqueError(DataBaseError): '''A location by that name is already in the database.''' diff --git a/DatabaseModels.py b/DatabaseModels.py index 271879d..8e0a983 100644 --- a/DatabaseModels.py +++ b/DatabaseModels.py @@ -11,6 +11,7 @@ from difflib import SequenceMatcher SQL_Base = declarative_base() + def check_similarity(a, b): ratio = SequenceMatcher(None, a, b).ratio() @@ -19,6 +20,7 @@ def check_similarity(a, b): else: return False + class DatabaseInterface: def __init__(self, db_engine_arg): @@ -34,6 +36,23 @@ class DatabaseInterface: self.database.add_object(shop) return shop + def add_tunnel(self, owner, color, number, location_name): + if location_name is None: + if len(self.find_tunnel_by_owner(owner)): + raise EntryNameNotUniqueError + else: + location = None + else: + try: + location = self.find_location_by_name_and_owner(owner, location_name)[0] + except IndexError: + raise LocationLookUpError + + tunnel = Tunnel(owner, color, number, location) + + self.database.add_object(tunnel) + return tunnel + def add_item(self, owner, shop_name, item_name, price, amount): try: shop = self.find_shop_by_name_and_owner(owner, shop_name) @@ -75,8 +94,8 @@ class DatabaseInterface: return self.database.query_by_filter(Location, expr) def find_location_by_owner_name(self, owner_name): - owner = self.find_player(owner_name) - return self.find_location_by_owner(owner) + expr = Location.owner.has(Player.name.ilike(owner_name)) + return self.database.query_by_filter(Location, expr) def find_shop_by_name_and_owner(self, owner, name): expr = (Shop.owner == owner) & (Shop.name.ilike(name)) @@ -93,6 +112,15 @@ class DatabaseInterface: return self.database.query_by_filter(Location, expr) + def find_tunnel_by_owner(self, owner): + expr = Tunnel.owner == owner + + return self.database.query_by_filter(Tunnel, expr) + + def find_tunnel_by_owner_name(self, owner_name): + expr = Tunnel.owner.has(Player.name.ilike(owner_name)) + return self.database.query_by_filter(Tunnel, expr) + def find_item(self, item_name): expr = ItemListing.name.ilike('%{}%'.format(item_name)) return self.database.query_by_filter(ItemListing, expr) @@ -157,6 +185,10 @@ class DiscordDatabaseInterface(DatabaseInterface): owner = DatabaseInterface.find_player_by_discord_uuid(self, owner_uuid) return DatabaseInterface.add_shop(self, owner, name, x_pos, y_pos, z_pos, dimension) + def add_tunnel(self, owner_uuid, color, number, location_name=""): + owner = DatabaseInterface.find_player_by_discord_uuid(self, owner_uuid) + return DatabaseInterface.add_tunnel(self, owner, color, number, location_name) + def add_item(self, owner_uuid, shop_name, item_name, price, amount): owner = DatabaseInterface.find_player_by_discord_uuid(self, owner_uuid) return DatabaseInterface.add_item(self, owner, shop_name, item_name, price, amount) @@ -209,7 +241,7 @@ class GeoffreyDatabase: self.session.add(obj) self.session.commit() except IntegrityError: - raise LocationNameNotUniqueError + raise EntryNameNotUniqueError def query_by_filter(self, obj_type, * args): filter_value = self.combine_filter(args) @@ -309,7 +341,7 @@ class Tunnel(SQL_Base): raise TunnelInitError def __str__(self): - return '{} {} {}'.format(self.tunnel_direction.value.title(), self.tunnel_number) + return '{} {}'.format(self.tunnel_direction.value.title(), self.tunnel_number) class Location(SQL_Base): __tablename__ = 'Locations' @@ -320,7 +352,7 @@ class Location(SQL_Base): y = Column(Integer) z = Column(Integer) - tunnel = relationship("Tunnel", back_populates="location") + tunnel = relationship("Tunnel", back_populates="location", uselist=False) dimension = Column(Enum(Dimension)) owner_id = Column(Integer, ForeignKey('Players.id')) @@ -369,6 +401,18 @@ class Shop(Location): 'polymorphic_identity': 'Shop', } + def inv_to_str(self): + inv = '' + str_format = '{}\n\t{}' + + for item in self.inventory: + inv = str_format.format(inv, item) + + return inv + + def __str__(self): + return Location.__str__(self) + "\n\t*Inventory*: {}".format(self.inv_to_str()) + def __init__(self, name, x, y, z, owner, dimension=None): Location.__init__(self, name, x, y, z, owner, dimension) diff --git a/Geoffrey.py b/Geoffrey.py index 33197b1..b3a1950 100644 --- a/Geoffrey.py +++ b/Geoffrey.py @@ -49,7 +49,7 @@ async def on_command_error(error, ctx): elif isinstance(error.original, PlayerNotFound): error_str = 'Make sure to use ?register first you ding dong.' database_interface.database.session.rollback() - elif isinstance(error.original, LocationNameNotUniqueError): + elif isinstance(error.original, EntryNameNotUniqueError): error_str = 'An entry in the database already has that name ding dong.' database_interface.database.session.rollback() else: @@ -65,6 +65,7 @@ async def test(): ''' await bot.say('I\'m here you ding dong') + @bot.command(pass_context=True) async def register(ctx): ''' @@ -86,43 +87,44 @@ async def register(ctx): async def addbase(ctx, x_pos: int, y_pos: int, z_pos: int, * args): ''' Adds your base to the database. The name is optional. - ?addbase [Shop Name] [X Coordinate] [Y Coordinate] [Z Coordinate] [Name] + ?addbase [X Coordinate] [Y Coordinate] [Z Coordinate] [Base Name] ''' if len(args) > 0: name = args[0] else: - name = '{}\'s Base'.format(database_interface.find_player_by_discord_uuid(ctx.message.author.id).name) + name = '{}\'s_Base'.format(database_interface.find_player_by_discord_uuid(ctx.message.author.id).name) try: base = database_interface.add_location(ctx.message.author.id, name, x_pos, y_pos, z_pos) except LocationInitError: raise commands.UserInputError - except LocationNameNotUniqueError: - await bot.say('{}, you already have a based called {}. You need to specify a different name.'.format( + except EntryNameNotUniqueError: + await bot.say('{}, a based called {} already exists. You need to specify a different name.'.format( ctx.message.author.mention, name)) return await bot.say('{}, your base named **{}** located at {} has been added' ' to the database.'.format(ctx.message.author.mention, base.name, base.pos_to_str())) + @bot.command(pass_context=True) async def addshop(ctx, x_pos: int, y_pos: int, z_pos: int, *args): ''' Adds your shop to the database. The name is optional. - ?addshop [Shop Name] [X Coordinate] [Y Coordinate] [Z Coordinate] [Name] + ?addshop [X Coordinate] [Y Coordinate] [Z Coordinate] [Shop Name] ''' if len(args) > 0: name = args[0] else: - name = '{}\'s Shop'.format(database_interface.find_player_by_discord_uuid(ctx.message.author.id).name) + name = '{}\'s_Shop'.format(database_interface.find_player_by_discord_uuid(ctx.message.author.id).name) try: shop = database_interface.add_shop(ctx.message.author.id, name, x_pos, y_pos, z_pos) except LocationInitError: raise commands.UserInputError - except LocationNameNotUniqueError: - await bot.say('{}, you already have a shop called {}. You need to specify a different name.'.format( + except EntryNameNotUniqueError: + await bot.say('{}, a shop called {} already exists. You need to specify a different name.'.format( ctx.message.author.mention, name)) return @@ -130,6 +132,35 @@ async def addshop(ctx, x_pos: int, y_pos: int, z_pos: int, *args): ' to the database.'.format(ctx.message.author.mention, shop.name, shop.pos_to_str())) +@bot.command(pass_context=True) +async def tunnel(ctx, tunnel_color: str, tunnel_number: int, *args): + ''' + Adds your tunnel to the database. The location name is optional. If the location has a tunnel, it is updated. + ?addtunnel [Tunnel Color] [Tunnel_Number] [Location Name] + ''' + + try: + if len(args) == 0: + location_name = None + else: + location_name = args[0] + + database_interface.add_tunnel(ctx.message.author.id, tunnel_color, tunnel_number, location_name) + except EntryNameNotUniqueError: + await bot.say('{}, you already have one tunnel in the database, please specify a location.'.format( + ctx.message.author.mention)) + return + except LocationLookUpError: + await bot.say('{}, you do not have a location called {}.'.format( + ctx.message.author.mention, args[0])) + return + + except ValueError: + raise commands.UserInputError + + await bot.say('{}, your tunnel has been added to the database'.format(ctx.message.author.mention)) + + @bot.command(pass_context=True) async def find(ctx, name: str): ''' @@ -146,6 +177,7 @@ async def find(ctx, name: str): except PlayerNotFound: await bot.say('{}, the player **{}** is not in the database'.format(ctx.message.author.mention, name)) + @bot.command(pass_context=True) async def delete(ctx, name: str): ''' @@ -219,7 +251,7 @@ async def additem(ctx, shop_name: str, item_name: str, amount: int, diamond_pric @bot.command(pass_context=True) -async def selling(ctx, item_name: str): +async def selling(item_name: str): ''' Lists all the shops selling an item @@ -232,20 +264,15 @@ async def selling(ctx, item_name: str): @bot.command(pass_context=True) -async def shopinfo(ctx, shop_name: str): +async def info(name: str): ''' - Lists the information and inventory of a shop + Displays a location's info including inventory its a shop - ?shopinfo [Shop Name] + ?info [Location Name] ''' - shop = database_interface.find_shop_by_name(shop_name)[0] - inv_list = database_interface.get_shop_inventory(shop) + loc = database_interface.find_location_by_name(name)[0] - item_list = '' - for item in inv_list: - item_list = item_list + '{}\n'.format(item.__str__()) - - await bot.say('{} \n Inventory:\n {}'.format(shop.__str__(), item_list)) + await bot.say('{}'.format(loc)) # Helper Functions ************************************************************ diff --git a/test_geoffreyDatabase.py b/test_geoffreyDatabase.py index 303a263..3dc5fb8 100644 --- a/test_geoffreyDatabase.py +++ b/test_geoffreyDatabase.py @@ -71,6 +71,14 @@ class TestGeoffreyDatabase(TestCase): self.assertEqual(loc_list[1].id, shop2.id) + def test_add_tunnel(self): + self.add_player() + args=[] + tunnel1 = self.interface.add_tunnel('143072699567177728', 'green', 155, None) + + tunnel2 = self.interface.find_tunnel_by_owner_name('ZeroHD')[0] + self.assertEqual(tunnel1, tunnel2) + def test_add_item(self): owner = self.add_player() self.add_shop() @@ -180,7 +188,7 @@ class TestGeoffreyDatabase(TestCase): self.add_player() self.add_loc() - self.assertRaises(LocationNameNotUniqueError, self.interface.add_location, + self.assertRaises(EntryNameNotUniqueError, self.interface.add_location, '143072699567177728', 'test', 0, 0, 0)