Geoffrey-Django/GeoffreyApp/models.py

548 lines
13 KiB
Python
Raw Normal View History

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
"""
2020-01-09 01:55:21 +00:00
discord_uuid = models.CharField(max_length=50, unique=True, blank=True, null=True)
"""
Discord UUID
"""
2020-10-10 17:06:10 +00:00
primary_location = models.OneToOneField("Location", on_delete=models.DO_NOTHING, blank=True, null=True)
"""
User's primary location
"""
@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",
"primary_location": "self.primary_location"
}
"""
2019-01-06 21:24:47 +00:00
return {"name": self.name,
"mc_uuid": self.mc_uuid,
"discord_uuid": self.discord_uuid,
"primary_location": None if self.primary_location is None else self.primary_location.id
}
@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,
2019-01-06 21:24:47 +00:00
"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
2020-01-08 20:56:14 +00:00
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):
2019-05-11 16:46:30 +00:00
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
{
2019-09-21 14:13:57 +00:00
"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"
2019-05-12 16:23:59 +00:00
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):
"""
2019-09-21 14:13:57 +00:00
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
}
"""
2019-01-06 21:24:47 +00:00
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)