diff --git a/admin.py b/admin.py index 8028060..2572f1d 100644 --- a/admin.py +++ b/admin.py @@ -8,3 +8,4 @@ admin.site.register(Shop) admin.site.register(Tunnel) admin.site.register(ItemListing) admin.site.register(APIToken) +admin.site.register(Town) diff --git a/api/commands.py b/api/commands.py index 4eec750..bc01eb5 100644 --- a/api/commands.py +++ b/api/commands.py @@ -6,18 +6,11 @@ import inspect import datetime import re +from GeoffreyApp.util import objects_list_to_json + command_dict = {"GET": {}, "POST": {}, "DELETE": {}} -def objects_list_to_json(obj_list): - json_list = [] - - for obj in obj_list: - json_list.append(obj.json) - - return json_list - - def match_tunnel(tunnel_direction): for direction in Tunnel.TUNNEL_NAMES: if re.search("{}.*".format(tunnel_direction), direction[1], re.IGNORECASE): @@ -161,6 +154,22 @@ def add_shop(x_pos, z_pos, name=None, discord_uuid=None, mc_uuid=None): return add_location(x_pos, z_pos, name=name, discord_uuid=discord_uuid, mc_uuid=mc_uuid, loc_type=Shop) +@command("POST") +def add_town(x_pos, z_pos, name=None, discord_uuid=None, mc_uuid=None): + ''' + :request: POST + :param x_pos: MC X Coordinate + :param z_pos: MC Z Coordinate + :param name: Shop Name (If None, Defaults to Player's Shop) + :param discord_uuid: Discord UUID + :param mc_uuid: Minecraft UUID + :return: JSON representation of the new town + :raises: EntryNameNotUniqueError, PlayerNotFound, LocationLookupError + :help: Adds your town to the database. The name is optional if this is your first town + ''' + + return add_location(x_pos, z_pos, name=name, discord_uuid=discord_uuid, mc_uuid=mc_uuid, loc_type=Town) + @command("POST") def add_tunnel(tunnel_direction, tunnel_number, location_name=None, discord_uuid=None, mc_uuid=None): ''' @@ -529,8 +538,8 @@ def add_owner(new_owner_name, location_name, discord_uuid=None, mc_uuid=None): :param location_name: The name of the location to add them to :param discord_uuid: Discord UUID of the current owner :param mc_uuid: MC UUID of the current owner - :return: Updated Location - :raises: PlayerNotFound, LocationLookupError, PlayerInDBError + :return: Update Location + :raises: PlayerNotFound, LocationLookupError, IsOwnerError, OwnerNotFound :help: Adds a co-owner to a location ''' owner = get_player(discord_uuid, mc_uuid) @@ -538,15 +547,72 @@ def add_owner(new_owner_name, location_name, discord_uuid=None, mc_uuid=None): try: new_owner = Player.objects.get(name__iexact=new_owner_name) except Player.DoesNotExist: - raise PlayerNotFound("New Owner Not in DB") + raise OwnerNotFoundError location = get_location(owner, location_name, Location) if location.owner.filter(location__owner__name__iexact=new_owner_name): - raise PlayerInDBError + raise IsOwnerError location.owner.add(new_owner) location.save() return location.json + + +@command("POST") +def add_resident(new_resident_name, town_name, discord_uuid=None, mc_uuid=None): + ''' + :request: POST + :param new_resident_name: The MC username of the new resident + :param town_name: The name of the town to add the resident to, can be blank if the owner has one town + :param discord_uuid: Discord UUID of the town owner + :param mc_uuid: MC UUID of the town owner + :return: Updated Location + :raises: PlayerNotFound, LocationLookupError, IsResidentError, ResidentNotFoundError + :help: Adds a resident to a town + ''' + owner = get_player(discord_uuid, mc_uuid) + + try: + new_resident = Player.objects.get(name__iexact=new_resident_name) + except Player.DoesNotExist: + raise ResidentNotFoundError + + town = get_location(owner, town_name, Town) + + if town.residents.filter(Q(town__owner__name__iexact=new_resident_name) | + Q(town__residents__name__iexact=new_resident_name)).all().count(): + raise IsResidentError + + town.residents.add(new_resident) + + town.save() + + return town.json + + +@command("POST") +def remove_resident(resident_name, town_name, discord_uuid=None, mc_uuid=None): + ''' + :request: POST + :param resident_name: Name of the resident to remove + :param town_name: Name of the town, can be blank if the owner has one town + :param discord_uuid: Owner discord uuid + :param mc_uuid: Owner mc uuid + :raises: PlayerNotFound, LocationLookupError, IsResidentError, ResidentNotFoundError + :return: Updated town + ''' + + owner = get_player(discord_uuid, mc_uuid) + town = get_location(owner, town_name, Town) + try: + resident = town.residents.get(town__residents__name__iexact=resident_name) + except Player.DoesNotExist: + raise ResidentNotFoundError + + town.residents.remove(resident) + + town.save() + return town.json diff --git a/errors.py b/errors.py index 0463aa8..4d8e244 100644 --- a/errors.py +++ b/errors.py @@ -86,5 +86,22 @@ class EmptryString(DataBaseError): class CommandNotFound(DataBaseError): """Command not found""" + class ExternalLookupFailed(DataBaseError): """Entry not found on external database""" + + +class IsOwnerError(DataBaseError): + """Player is already an owner of the location""" + + +class IsResidentError(DataBaseError): + """Player is already a resident in the town""" + + +class ResidentNotFoundError(DataBaseError): + """No resident matches""" + + +class OwnerNotFoundError(DataBaseError): + """No owner matches""" diff --git a/migrations/0007_town.py b/migrations/0007_town.py new file mode 100644 index 0000000..e079414 --- /dev/null +++ b/migrations/0007_town.py @@ -0,0 +1,23 @@ +# Generated by Django 2.1.2 on 2019-02-02 20:57 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ('GeoffreyApp', '0006_auto_20190131_1822'), + ] + + operations = [ + migrations.CreateModel( + name='Town', + fields=[ + ('location_ptr', + models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, + primary_key=True, serialize=False, to='GeoffreyApp.Location')), + ('residents', models.ManyToManyField(to='GeoffreyApp.Player')), + ], + bases=('GeoffreyApp.location',), + ), + ] diff --git a/models.py b/models.py index e58d3b2..3aa20f9 100644 --- a/models.py +++ b/models.py @@ -2,7 +2,7 @@ from django.db import models from django.conf import settings from sys import maxsize -from GeoffreyApp.util import create_token +from GeoffreyApp.util import create_token, objects_list_to_json # Create your models here. @@ -129,6 +129,8 @@ class Location(models.Model): return "shop" elif hasattr(self, "base"): return "base" + elif hasattr(self, "town"): + return "town" else: return "location" @@ -157,6 +159,27 @@ class Base(Location): return "Base: %s" % self.name +class Town(Location): + residents = models.ManyToManyField(Player) + + def __str__(self): + return "Town: %s" % self.name + + @property + def get_residents(self): + residents = self.residents.all() + + return objects_list_to_json(residents) + + @property + def json(self): + json = super().json + + json["residents"] = self.residents + + return json + + class ItemListing(models.Model): item_name = models.CharField(max_length=128) ''' diff --git a/test/test_commands.py b/test/test_commands.py index 30e9258..b62071f 100644 --- a/test/test_commands.py +++ b/test/test_commands.py @@ -28,6 +28,11 @@ class CommandsAPITestCase(TestCase): self.base.owner.add(self.player) self.shop = Shop.objects.create(name="test shop", x_coord=500, z_coord=500, dimension="O") + + self.town = Town.objects.create(name="test town", x_coord=-500, z_coord=-500, + dimension="O") + + self.town.owner.add(self.player) self.shop.owner.add(self.player) self.item = ItemListing.objects.create(shop=self.shop, price=1, amount=5, item_name="sed") @@ -58,6 +63,13 @@ class CommandsAPITestCase(TestCase): self.assertEqual(shop.owner.all()[0].name, "ZeroHD") + def test_add_town(self): + add_town(x_pos=0, z_pos=0, name=None, discord_uuid=DISCORD_UUID) + + town = Town.objects.filter(name__icontains=USERNAME).all().first() + + self.assertEqual(town.owner.all()[0].name, "ZeroHD") + def test_add_tunnel(self): base = Base.objects.create(name="Test", x_coord=0, z_coord=0) @@ -93,7 +105,7 @@ class CommandsAPITestCase(TestCase): count = len(locations) - self.assertEqual(count, 2) + self.assertGreater(count, 1) def test_find_around(self): self.populate() @@ -126,16 +138,18 @@ class CommandsAPITestCase(TestCase): locations = me(discord_uuid=DISCORD_UUID) - self.assertEqual(len(locations), 2) + self.assertGreater(len(locations), 1) def test_delete(self): self.populate() + count = Location.objects.filter(name__icontains="test").all().count() + delete(name="test", discord_uuid=DISCORD_UUID) locations = Location.objects.filter(name__icontains="test").all() - self.assertEqual(len(locations), 1) + self.assertEqual(len(locations), count - 1) def test_delete_item(self): self.populate() @@ -168,3 +182,29 @@ class CommandsAPITestCase(TestCase): self.assertEquals(shop.id, self.shop.id) self.assertRaises(PlayerInDBError, add_owner, new_owner.name, self.shop.name, discord_uuid=DISCORD_UUID) + + def test_add_resident(self): + self.populate() + new_resident = Player.objects.create(name="Vakky", mc_uuid="5", discord_uuid="5") + add_resident(new_resident.name, self.town.name, discord_uuid=DISCORD_UUID) + + town = Town.objects.get(residents__name__icontains="Vakky") + + self.assertEquals(town.id, self.town.id) + + self.assertRaises(PlayerInDBError, add_resident, new_resident.name, self.town.name, discord_uuid=DISCORD_UUID) + self.assertRaises(PlayerInDBError, add_resident, self.player.name, self.town.name, discord_uuid=DISCORD_UUID) + + def test_remove_resident(self): + self.populate() + new_resident = Player.objects.create(name="Vakky", mc_uuid="5", discord_uuid="5") + add_resident(new_resident.name, self.town.name, discord_uuid=DISCORD_UUID) + + Town.objects.get(residents__name__icontains="Vakky") + + remove_resident(new_resident.name, self.town.name, discord_uuid=DISCORD_UUID) + + self.assertRaises(Town.DoesNotExist, Town.objects.get, residents__name__icontains="Vakky") + + self.assertRaises(ResidentNotFoundError, remove_resident, new_resident.name, self.town.name, + discord_uuid=DISCORD_UUID) diff --git a/util.py b/util.py index 28d5288..1893c68 100644 --- a/util.py +++ b/util.py @@ -10,3 +10,12 @@ def create_token(length=25): token += d return token + + +def objects_list_to_json(obj_list): + json_list = [] + + for obj in obj_list: + json_list.append(obj.json) + + return json_list