From f8ae09fd56507f45958f7a9e93f81ac796b5e5da Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Sat, 2 Mar 2019 11:48:28 -0600 Subject: [PATCH] Added public farm location type +Added PublicFarm and Resource models +Farms have resources that they can harvest +Added commands to add farms and add/remove resources from them +Added find_farm to find farms --- admin.py | 2 + api/commands.py | 77 +++++++++++++++++++- errors.py | 4 + migrations/0008_publicfarm_resource.py | 31 ++++++++ models.py | 28 +++++-- templates/GeoffreyApp/itemlisting_table.html | 2 +- templates/GeoffreyApp/location_link.html | 5 -- templates/GeoffreyApp/publicfarm.html | 7 ++ templates/GeoffreyApp/publicfarm_list.html | 9 +++ templates/GeoffreyApp/resource_table.html | 20 +++++ templatetags/navbar.py | 3 +- test/test_commands.py | 36 +++++++++ urls.py | 2 + views.py | 25 +++++++ 14 files changed, 238 insertions(+), 13 deletions(-) create mode 100644 migrations/0008_publicfarm_resource.py delete mode 100644 templates/GeoffreyApp/location_link.html create mode 100644 templates/GeoffreyApp/publicfarm.html create mode 100644 templates/GeoffreyApp/publicfarm_list.html create mode 100644 templates/GeoffreyApp/resource_table.html diff --git a/admin.py b/admin.py index 2572f1d..8e61638 100644 --- a/admin.py +++ b/admin.py @@ -9,3 +9,5 @@ admin.site.register(Tunnel) admin.site.register(ItemListing) admin.site.register(APIToken) admin.site.register(Town) +admin.site.register(PublicFarm) +admin.site.register(Resource) diff --git a/api/commands.py b/api/commands.py index c0f379c..2ea6a80 100644 --- a/api/commands.py +++ b/api/commands.py @@ -175,6 +175,25 @@ def add_town(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=Town) + +@command("POST") +def add_farm(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 public farm to the database. + ''' + + return add_location(x_pos, z_pos, name=name, discord_uuid=discord_uuid, mc_uuid=mc_uuid, loc_type=PublicFarm) + + + @command("POST") def add_tunnel(tunnel_direction, tunnel_number, location_name=None, discord_uuid=None, mc_uuid=None): ''' @@ -296,6 +315,36 @@ def add_item(item_name, quantity, diamond_price, shop_name=None, discord_uuid=No return item_listing.json +@command("POST") +def add_resource(resource_name, farm_name=None, discord_uuid=None, mc_uuid=None): + ''' + :request: POST + :param resource_name: name of the resource + ;param farm_name: name of the farm to add the resource to. Can be none. + :param discord_uuid: Discord UUID + :param mc_uuid: Minecraft UUID + :return: Item Listing + :raises: PlayerNotFound, LocationLookupError, EntryNameNotUniqueError, NoLocationsInDatabase + :help: Adds a resource to a farm. + ''' + + player = get_player(discord_uuid, mc_uuid) + + farm = get_location(player, farm_name, PublicFarm).publicfarm + + resource = Resource.objects.create(farm=farm, resource_name=resource_name) + + return resource.json + + +@command("GET") +def find_farm(resource_name): + if len(resource_name) == 0: + raise EmptryString + + return objects_list_to_json(PublicFarm.objects.filter(resource__resource_name__icontains=resource_name).all()) + + @command("GET") def selling(item_name): ''' @@ -323,7 +372,6 @@ def selling_price(item_name): def get_selling(item_name, sort): - items = [] if len(item_name) == 0: raise EmptryString @@ -513,6 +561,33 @@ def delete_item(item, shop_name=None, discord_uuid=None, mc_uuid=None): return shop.json +@command("POST") +def delete_resource(resource_name, farm_name=None, discord_uuid=None, mc_uuid=None): + ''' + :request: POST + :param resource: resource to delete + :param farm_name: Farm with resource, can be None if the user only has one farm + :param discord_uuid: Discord UUID + :param mc_uuid: Minecraft UUID + :return: PublicFarm where the resource was deleted from + :raises: PlayerNotFound, LocationLookupError, EntryNameNotUniqueError, NoLocationsInDatabase, ItemNotFound + :help: Deletes a resource from a farm + ''' + + player = get_player(discord_uuid=discord_uuid, mc_uuid=mc_uuid) + + farm = get_location(player, farm_name, PublicFarm) + + delete_list = Resource.objects.filter(resource_name=resource_name, farm=farm).all() + + if len(delete_list) == 0: + raise ResourceNotFoundError + + delete_list.delete() + + return farm.json + + @command("GET") def me(discord_uuid=None, mc_uuid=None): ''' diff --git a/errors.py b/errors.py index 4d8e244..20f5bf8 100644 --- a/errors.py +++ b/errors.py @@ -105,3 +105,7 @@ class ResidentNotFoundError(DataBaseError): class OwnerNotFoundError(DataBaseError): """No owner matches""" + + +class ResourceNotFoundError(DataBaseError): + """No resource found""" diff --git a/migrations/0008_publicfarm_resource.py b/migrations/0008_publicfarm_resource.py new file mode 100644 index 0000000..6a0bcda --- /dev/null +++ b/migrations/0008_publicfarm_resource.py @@ -0,0 +1,31 @@ +# Generated by Django 2.1.2 on 2019-03-01 20:28 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ('GeoffreyApp', '0007_town'), + ] + + operations = [ + migrations.CreateModel( + name='PublicFarm', + 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')), + ], + bases=('GeoffreyApp.location',), + ), + migrations.CreateModel( + name='Resource', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('resource_name', models.CharField(max_length=128)), + ('farm', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='resource', + to='GeoffreyApp.PublicFarm')), + ], + ), + ] diff --git a/models.py b/models.py index 3412cf1..e96250c 100644 --- a/models.py +++ b/models.py @@ -112,10 +112,6 @@ class Location(models.Model): return owner_list - @property - def link(self): - return "" - @property def json(self): return {"type": self.__class__.__name__, @@ -137,7 +133,10 @@ class Location(models.Model): @property def link(self): - return self.loc_child_obj.link + child = self.loc_child_obj + + if child != self: + return self.loc_child_obj.link @property def loc_child_obj(self): @@ -147,6 +146,8 @@ class Location(models.Model): return self.base elif hasattr(self, "town"): return self.town + elif hasattr(self, "publicfarm"): + return self.publicfarm else: return self @@ -208,6 +209,23 @@ class Town(Location): return reverse("GeoffreyTownInfo", kwargs={"id": self.id}) +class PublicFarm(Location): + @property + def link(self): + return reverse("GeoffreyPublicFarmInfo", kwargs={"id": self.id}) + + +class Resource(models.Model): + farm = models.ForeignKey(PublicFarm, related_name="resource", on_delete=models.CASCADE) + resource_name = models.CharField(max_length=128) + + @property + def json(self): + return {"name": self.resource_name, + "farm_id": self.farm_id + } + + class ItemListing(models.Model): item_name = models.CharField(max_length=128) ''' diff --git a/templates/GeoffreyApp/itemlisting_table.html b/templates/GeoffreyApp/itemlisting_table.html index 9d161a3..8f6bc6c 100644 --- a/templates/GeoffreyApp/itemlisting_table.html +++ b/templates/GeoffreyApp/itemlisting_table.html @@ -20,7 +20,7 @@ {{ item.normalized_price }}D {% if show_shop %} {{ item.shop.location }} - {% include "GeoffreyApp/location_link.html" with loc=item.shop %} + {{ item.shop.name }} {% endif %} {% endfor %} diff --git a/templates/GeoffreyApp/location_link.html b/templates/GeoffreyApp/location_link.html deleted file mode 100644 index 7913ec8..0000000 --- a/templates/GeoffreyApp/location_link.html +++ /dev/null @@ -1,5 +0,0 @@ -{% if loc.loc_type == "Base" %} - {{ loc.name }} -{% elif loc.loc_type == "Shop" %} - {{ loc.name }} -{% endif %} diff --git a/templates/GeoffreyApp/publicfarm.html b/templates/GeoffreyApp/publicfarm.html new file mode 100644 index 0000000..10d5d09 --- /dev/null +++ b/templates/GeoffreyApp/publicfarm.html @@ -0,0 +1,7 @@ +{% extends "GeoffreyApp/location.html" %} + +{% block info %} +

Resources:

+ {% include "GeoffreyApp/resource_table.html" with resource_list=resources %} +
+{% endblock %} \ No newline at end of file diff --git a/templates/GeoffreyApp/publicfarm_list.html b/templates/GeoffreyApp/publicfarm_list.html new file mode 100644 index 0000000..deb6526 --- /dev/null +++ b/templates/GeoffreyApp/publicfarm_list.html @@ -0,0 +1,9 @@ +{% extends "GeoffreyApp/base.html" %} + +{% block header %} + Public Farms +{% endblock %} + +{% block content %} + {% include "GeoffreyApp/location_table.html" with loc_list=publicfarm_list show_owner=True %} +{% endblock %} diff --git a/templates/GeoffreyApp/resource_table.html b/templates/GeoffreyApp/resource_table.html new file mode 100644 index 0000000..a5986a9 --- /dev/null +++ b/templates/GeoffreyApp/resource_table.html @@ -0,0 +1,20 @@ + + + + + + + + {% for resource in resource_list %} + + + + {% endfor %} + + + + \ No newline at end of file diff --git a/templatetags/navbar.py b/templatetags/navbar.py index a1e656d..90a5a51 100644 --- a/templatetags/navbar.py +++ b/templatetags/navbar.py @@ -9,7 +9,8 @@ navbar_options = [ ("Shops", reverse("GeoffreyShops")), ("Bases", reverse("GeoffreyBases")), ("Towns", reverse("GeoffreyTowns")), - ("Item Listings", reverse("GeoffreyItems")) + ("Item Listings", reverse("GeoffreyItems")), + ("Public Farms", reverse("GeoffreyPublicFarms")) ] option_format = '' diff --git a/test/test_commands.py b/test/test_commands.py index 1ef84af..614671c 100644 --- a/test/test_commands.py +++ b/test/test_commands.py @@ -22,6 +22,7 @@ class CommandsAPITestCase(TestCase): Shop.objects.all().delete() ItemListing.objects.all().delete() Tunnel.objects.all().delete() + PublicFarm.objects.all().delete() def populate(self): self.base = Base.objects.create(name="test", x_coord=0, z_coord=0, dimension="O") @@ -38,6 +39,9 @@ class CommandsAPITestCase(TestCase): self.tunnel = Tunnel.objects.create(tunnel_number="42", tunnel_direction="S", location=self.base) + self.farm = PublicFarm.objects.create(name="test farm", x_coord=0, z_coord=0, dimension="O") + self.farm.owner.add(self.player) + def test_register(self): register(player_name="Vakky", discord_uuid="229423434256351233") @@ -208,3 +212,35 @@ class CommandsAPITestCase(TestCase): self.assertRaises(ResidentNotFoundError, remove_resident, new_resident.name, self.town.name, discord_uuid=DISCORD_UUID) + + def test_add_resource(self): + self.populate() + + resource = add_resource(resource_name="sed", farm_name="test farm", discord_uuid=DISCORD_UUID) + + farm = PublicFarm.objects.filter(resource__resource_name__icontains="sed").first() + + self.assertEqual(resource["farm_id"], farm.id) + + def test_remove_resource(self): + self.populate() + + resource = add_resource(resource_name="sed", farm_name="test farm", discord_uuid=DISCORD_UUID) + + delete_resource(resource_name="sed", farm_name="test farm", discord_uuid=DISCORD_UUID) + + farm = PublicFarm.objects.filter(resource__resource_name__icontains="sed").all() + + self.assertEqual(len(farm), 0) + + def test_find_farm(self): + self.populate() + add_resource(resource_name="sed", farm_name="test farm", discord_uuid=DISCORD_UUID) + + list = find_farm(resource_name="sed") + + for farm in list: + if farm["name"] == "test farm": + return + + self.fail("Farm not found") diff --git a/urls.py b/urls.py index c0672aa..6510367 100644 --- a/urls.py +++ b/urls.py @@ -13,5 +13,7 @@ urlpatterns = [ url(r'^towns/(?P[0-9]{1,9})/$', views.TownInfo.as_view(), name='GeoffreyTownInfo'), url(r'^items/$', views.ItemListingList.as_view(), name='GeoffreyItems'), url(r'^search/$', views.SearchList.as_view(), name='GeoffreySearch'), + url(r'^farm/$', views.PublicFarmList.as_view(), name='GeoffreyPublicFarms'), + url(r'^farm/(?P[0-9]{1,9})/$', views.PublicFarmInfo.as_view(), name='GeoffreyPublicFarmInfo') ] diff --git a/views.py b/views.py index 08f5907..790041f 100644 --- a/views.py +++ b/views.py @@ -81,6 +81,15 @@ class TownList(generic.ListView): return context +class PublicFarmList(generic.ListView): + model = PublicFarm + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['current_page'] = "Public Farms" + return context + + class ItemListingList(generic.ListView): model = ItemListing @@ -147,3 +156,19 @@ class TownInfo(View): return render(request, 'GeoffreyApp/town.html', context=context) except Player.DoesNotExist: return render(request, 'GeoffreyApp/error.html') + + +class PublicFarmInfo(View): + def get(self, request, id): + try: + public_farm = PublicFarm.objects.get(pk=id) + + resources = public_farm.resource.all() + + context = { + "loc": public_farm, + "resources": resources + } + return render(request, 'GeoffreyApp/publicfarm.html', context=context) + except Player.DoesNotExist: + return render(request, 'GeoffreyApp/error.html')