from django.db import models from django.conf import settings from django.urls import reverse from sys import maxsize import datetime import enum from GeoffreyApp.util import create_token, objects_list_to_json # Create your models here. class PermissionLevel(enum.Enum): ADMIN = enum.auto() MOD = enum.auto() PLAYER = enum.auto() class APIToken(models.Model): key = models.CharField(default=create_token, max_length=25, unique=True) """ Key used to access the Geoffrey API """ name = models.CharField(max_length=50, blank=True) """ Name of the key """ commands_perm = models.BooleanField(default=False) """ Permission to use the command api """ mod_commands_perm = models.BooleanField(default=False) """ Permission to use mod commands """ admin_commands_perm = models.BooleanField(default=False) """ Permission to use admin commands """ model_api_perm = models.BooleanField(default=False) """ Permission to access the model api """ def has_command_permission(self, permission_level): if permission_level == PermissionLevel.ADMIN: return self.admin_commands_perm elif permission_level == PermissionLevel.MOD: return self.admin_commands_perm elif permission_level == PermissionLevel.PLAYER: return self.commands_perm else: return False def __str__(self): if len(self.name): return "{}: {}".format(self.name, self.key) else: return self.key class Player(models.Model): """ Model of a Player """ name = models.CharField(max_length=30, unique=True) """ In Game Username """ mc_uuid = models.CharField(max_length=36, unique=True) """ Minecraft UUID """ discord_uuid = models.CharField(max_length=50, unique=True, blank=True) """ Discord UUID """ @property def loc_count(self): """ Number of locations the player is an owner of """ return Location.objects.filter(owner=self).count() @property def json(self): """ JSON representation of the Player .. code-block:: json { "name" : "self.name", "mc_uuid": "self.mc_uuid", "discord_uuid": "self.discord_uuid", } """ return {"name": self.name, "mc_uuid": self.mc_uuid, "discord_uuid": self.discord_uuid } @property def link(self): """ href to the player page """ return reverse("GeoffreyPlayerInfo", kwargs={"id": self.id}) def __str__(self): return self.name class Location(models.Model): """Model of a Location""" info_page = None """ Name of the info page view """ DIMENSIONS = ( ('O', 'Overworld'), ('N', 'Nether'), ('E', 'The End') ) """ Possible dimensions for a location to be in """ name = models.CharField(max_length=128, unique=True) """ Name of the location """ x_coord = models.IntegerField() """ X Position """ z_coord = models.IntegerField() """ Z Position """ dimension = models.CharField(max_length=1, choices=DIMENSIONS) """ Dimension of Location """ owner = models.ManyToManyField(Player) """ Owner of Location """ @property def position(self): """ Formatted position of the location """ return "(x={}, z={})".format(self.x_coord, self.z_coord) @property def tunnel(self): """ The tunnel associated if this location, None if no tunnel exists """ try: tunnel = Tunnel.objects.get(location=self) except Tunnel.DoesNotExist: tunnel = None return tunnel @property def get_owners(self): """ List of all the owners of the location """ owner_list = [] for owner in self.owner.all(): owner_list.append(owner.json) return owner_list @property def json(self): """ JSON representation of the location .. code-block:: json { "type": "Base", "name": "Location", "x_coord": 0, "z_coord": 0, "dimension": "O", "owner": [], "tunnel": {}, "link": "/GeoffreyApp/Base/1" } """ return {"type": self.loc_type, "name": self.name, "x_coord": self.x_coord, "z_coord": self.z_coord, "dimension": self.dimension, "owner": self.get_owners, "location": self.position, "tunnel": None if self.tunnel is None else self.tunnel.tunnel_str, "link": self.link } @property def loc_type(self): """ The name of the location type """ str = self.loc_child_obj.__class__.__name__ return str @property def link(self): """ href to the location page """ child = self.loc_child_obj if child is not self: return reverse(child.info_page, kwargs={"id": child.id}) else: return reverse(self.info_page, kwargs={"id": self.id}) @property def loc_child_obj(self): """ Child object """ if hasattr(self, "shop"): return self.shop elif hasattr(self, "base"): return self.base elif hasattr(self, "town"): return self.town elif hasattr(self, "publicfarm"): return self.publicfarm elif hasattr(self, "market"): return self.market elif hasattr(self, "attraction"): return self.attraction elif hasattr(self, "pointofinterest"): return self.pointofinterest else: return self @property def dynmap_url(self): """ Link to the location on the dynmap, none if there is no dynmap """ base_url = getattr(settings, "GEOFFREY_DYNMAP_BASE_URL") world_name = getattr(settings, "GEOFFREY_DYNMAP_WORLD_NAME") if base_url: url = base_url + "/?worldname={}&mapname=surface&zoom=4&x={}&y=65&z={}".format(world_name, self.x_coord, self.z_coord) return url else: return None def __str__(self): return "%s: %s" % (self.loc_child_obj.__class__.__name__, self.name) class PointOfInterest(Location): info_page = "GeoffreyPointOfInterestInfo" class Shop(Location): info_page = "GeoffreyShopInfo" class Base(Location): info_page = "GeoffreyBaseInfo" class Town(Location): info_page = "GeoffreyTownInfo" residents = models.ManyToManyField(Player) """ Players who are members of the town """ @property def get_residents(self): """ List of residents in the town in JSON farm """ residents = self.residents.all() return objects_list_to_json(residents) @property def json(self): """ JSON representation of the town .. code-block:: json { "type": "Town", "name": "Location", "x_coord": 0, "z_coord": 0, "dimension": "O", "owner": [], "tunnel": {}, "link": "/GeoffreyApp/Town/1", "residents": [] } """ json = super().json json["residents"] = self.get_residents return json class Market(Location): info_page = "GeoffreyMarketInfo" def get_shops(self, market_radius=100, limit=10): x = int(self.x_coord) z = int(self.z_coord) shops = Shop.objects.filter(x_coord__range=(x - market_radius, x + market_radius), z_coord__range=(z - market_radius, z + market_radius) )[:limit] return shops @property def json(self): """ JSON representation of the market .. code-block:: json { "type": "Market", "name": "Location", "x_coord": 0, "z_coord": 0, "dimension": "O", "owner": [], "tunnel": {}, "link": "/GeoffreyApp/Market/1", "shops": [] } """ json = super().json json["shops"] = objects_list_to_json(self.get_shops(limit=10)) return json class PublicFarm(Location): info_page = "GeoffreyPublicFarmInfo" class Attraction(Location): info_page = "GeoffreyAttractionInfo" 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): """ JSON representation of the Resource .. code-block:: json { "name": "Dirt" "farm_id": 1 } """ return {"name": self.resource_name, "farm_id": self.farm_id } class ItemListing(models.Model): item_name = models.CharField(max_length=128) """ Name of the item """ price = models.IntegerField() """ Number of diamonds per amount of items """ amount = models.IntegerField() """ Number of items """ date_restocked = models.DateTimeField(auto_now=True) """ Datetime the item was last restocked """ shop = models.ForeignKey(Shop, related_name="shop_selling", on_delete=models.CASCADE) """ Shop the item is sold at """ @property def normalized_price(self): """ normalized price, price/amount """ if self.amount == 0: return maxsize else: return self.price / self.amount @property def number_of_days_since_restocked(self): """ days since this item was restocked """ days = (datetime.datetime.now().astimezone() - self.date_restocked.astimezone()).days return days @property def json(self): """ JSON representation of the item .. code-block:: json { "item_name": "dirt", "price": 1, "amount": 1, "date_restocked": 1553264508, "days_since_restock": 10, "normalized_price": 1, "shop": {} } """ return {"item_name": self.item_name, "price": self.price, "amount": self.amount, "date_restocked": self.date_restocked.timestamp(), "days_since_restock": self.number_of_days_since_restocked, "normalized_price": self.normalized_price, "shop": self.shop.json, } def __str__(self): return "Item: %d %s for %d" % (self.amount, self.item_name, self.amount) class Tunnel(models.Model): TUNNEL_NAMES = ( ('N', getattr(settings, 'GEOFFREY_NORTH_TUNNEL', '')), ('E', getattr(settings, 'GEOFFREY_EAST_TUNNEL', '')), ('S', getattr(settings, 'GEOFFREY_SOUTH_TUNNEL', '')), ('W', getattr(settings, 'GEOFFREY_WEST_TUNNEL', '')) ) """ Tunnel Direction """ tunnel_number = models.IntegerField() """ Tunnel coordinate """ tunnel_direction = models.CharField(max_length=1, choices=TUNNEL_NAMES) """ Tunnel Direction """ location = models.ForeignKey(Location, related_name="tunnel_location", on_delete=models.CASCADE) """ Location that the tunnel is connected to """ def __str__(self): return "Tunnel: %s %d" % (self.get_tunnel_direction_display(), self.tunnel_number) @property def json(self): """ JSON representation of the tunnel` .. code-block:: json { "location_name": "Base", "tunnel_direction": "N", "tunnel_number": 500 } """ return {"location_name": self.location.name, "tunnel_direction": self.get_tunnel_direction_display(), "tunnel_number": self.tunnel_number } @property def tunnel_str(self): """ formatted tunnel string """ return "{} {}".format(self.get_tunnel_direction_display(), self.tunnel_number)