Results-Page/apps/home/models.py
2024-11-05 11:48:32 +00:00

482 lines
15 KiB
Python

"""Models for the mainapp."""
from django.db import models
from django.utils import timezone
from django.core.exceptions import ValidationError
# region Generic Models
class GenericTypeModel(models.Model):
name = models.CharField(max_length=128)
class Meta:
abstract = True
ordering = ("name",)
def __str__(self):
return self.name
# region Anglers & Groups
class Angler(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=128)
redact = models.BooleanField()
def __str__(self):
return self.name
class AnglerGroupType(GenericTypeModel):
id = models.AutoField(primary_key=True)
class AnglerGroup(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=128)
anglers = models.ForeignKey(to=Angler, on_delete=models.CASCADE)
type = models.ForeignKey(to=AnglerGroupType, on_delete=models.CASCADE)
def __str__(self):
return f"{self.name} ({self.anglers.count} anglers)"
# region Venues & Waters
class FishType(GenericTypeModel):
id = models.AutoField(primary_key=True)
class WatersType(GenericTypeModel):
id = models.AutoField(primary_key=True)
class Waters(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=128)
pegs_from = models.IntegerField()
pegs_to = models.IntegerField()
map = models.ImageField()
type = models.ForeignKey(to=WatersType, on_delete=models.CASCADE)
fish_types = models.ForeignKey(to=FishType, on_delete=models.CASCADE)
def __str__(self):
return self.name
class VenueType(GenericTypeModel):
id = models.AutoField(primary_key=True)
class VenueAddress(models.Model):
id = models.AutoField(primary_key=True)
street_number = models.IntegerField()
street_address = models.CharField(max_length=256)
town = models.CharField(max_length=256)
county = models.CharField(max_length=256)
post_code = models.CharField(max_length=32)
satnav_post_code = models.CharField(max_length=32)
country = models.CharField(max_length=128)
latitude = models.DecimalField(max_digits=22, decimal_places=16)
longitude = models.DecimalField(max_digits=22, decimal_places=16)
def __str__(self):
return f"{self.street_address}, {self.town} ({self.country})"
class VenueContacts(models.Model):
id = models.AutoField(primary_key=True)
phone_number = models.CharField(max_length=64)
email_address = models.EmailField()
website_url = models.URLField()
facebook_url = models.URLField()
twitter_url = models.URLField()
instagram_url = models.URLField()
def __str__(self):
return self.email_address
class Venue(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=128)
description = models.CharField(max_length=384)
extra_notes = models.CharField(max_length=1028)
created_at = models.DateTimeField(default=timezone.now, editable=False)
updated_at = models.DateTimeField(default=timezone.now, editable=False)
profile_picture = models.ImageField()
banner_picture = models.ImageField()
type = models.ForeignKey(to=VenueType, on_delete=models.CASCADE)
address = models.ForeignKey(to=VenueAddress, on_delete=models.CASCADE)
contacts = models.ForeignKey(to=VenueContacts, on_delete=models.CASCADE)
waters = models.ManyToManyField(to=Waters)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
self.updated_at = timezone.now()
return super().save(*args, **kwargs)
# region Leagues & Matches
class CompetitorType(GenericTypeModel):
id = models.AutoField(primary_key=True)
class MatchType(GenericTypeModel):
id = models.AutoField(primary_key=True)
class Match(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=128)
description = models.CharField(max_length=384)
venue = models.ForeignKey(to=Venue, on_delete=models.CASCADE)
waters = models.ForeignKey(to=Waters, on_delete=models.CASCADE) # can only select waters from the matching venue
meeting_point = models.CharField(max_length=1024)
use_metric = models.BooleanField()
allow_in_tournaments = models.BooleanField()
start_datetime = models.DateTimeField()
end_datetime = models.DateTimeField()
draw_datetime = models.DateTimeField()
type = models.ForeignKey(to=MatchType, on_delete=models.CASCADE)
competitor_type = models.ForeignKey(to=CompetitorType, on_delete=models.CASCADE)
class LeagueRule(models.Model):
pass
class League(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=128)
description = models.CharField(max_length=384)
extra_notes = models.CharField(max_length=1028)
profile_picture = models.ImageField()
banner_picture = models.ImageField()
matches = models.ManyToManyField(to=Match)
anglers = models.ManyToManyField(to=Angler)
rules = models.ManyToManyField(to=LeagueRule)
def __str__(self):
return self.name
@property
def results(self):
return LeagueResult.objects.filter(league=self)
class Sponsor(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=128)
url = models.URLField()
image = models.ImageField()
def __str__(self):
return self.name
class LeagueResult(models.Model):
id = models.AutoField(primary_key=True)
league = models.ForeignKey(to=League, on_delete=models.CASCADE)
angler = models.ForeignKey(to=Angler, on_delete=models.CASCADE)
sponsor = models.ForeignKey(to=Sponsor, on_delete=models.CASCADE, null=True, blank=True)
total_weight = models.CharField(max_length=64)
matches = models.IntegerField()
date = models.DateField(default=timezone.now)
def __str__(self):
return f"{self.league.name} - {self.angler.name}"
# class Venue(models.Model):
# """Represents a Venue and Waters."""
# VENUE_TYPES = (
# ("FISHERY", "Fishery"),
# ("CLUB", "Club"),
# ("PRIVATE", "Private")
# )
# name = models.CharField(max_length=255, null=True, blank=True)
# description = models.TextField(blank=True, max_length=500, null=True,)
# extra_notes = models.TextField(blank=True, null=True,)
# venue_type = models.CharField(choices=VENUE_TYPES, max_length=50, null=True, blank=True)
# # Contact information
# phone_number = models.CharField(max_length=100, null=True, blank=True)
# email_address = models.EmailField(null=True, blank=True)
# website_url = models.URLField(null=True, blank=True)
# # Location information
# street_address = models.CharField(max_length=100, null=True, blank=True)
# city = models.CharField(max_length=255, null=True, blank=True)
# provence = models.CharField(max_length=100, null=True, blank=True)
# postal_code = models.CharField(max_length=20, null=True, blank=True)
# country = models.CharField(max_length=100, null=True, blank=True)
# latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
# longitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
# # Socials information
# twitter_url = models.URLField(blank=True, null=True)
# instagram_url = models.URLField(blank=True, null=True)
# facebook_url = models.URLField(blank=True, null=True)
# active = models.BooleanField(default=True)
# def __str__(self):
# return self.name
# def waters(self):
# """Returns all waters linked to this venue."""
# waters = Waters.objects.filter(venue=self)
# return waters
# class Waters(models.Model):
# """Represents the waters of a Venue"""
# WATER_TYPES = (
# ("CW", "Commercial Water"),
# ("NSW", "Natural Still Water"),
# ("C", "Canal"),
# ("R", "River"),
# ("L", "Loch"),
# )
# FISH_TYPES = (
# ("C", "Coarse"),
# ("SC", "Specimen Carp"),
# ("G", "Game"),
# ("P", "Predator"),
# )
# venue = models.ForeignKey(Venue, on_delete=models.CASCADE)
# name = models.CharField(max_length=100)
# description = models.TextField(max_length=255)
# pegs_min = models.IntegerField()
# pegs_max = models.IntegerField()
# water_type = models.CharField(choices=WATER_TYPES, max_length=50)
# fish_type = models.CharField(choices=FISH_TYPES, max_length=50)
# # water_map = models.ImageField()
# def __str__(self):
# return self.name
# class ReusableAutoField(models.PositiveIntegerField):
# """A django auto field that can reuse deleted primary keys."""
# def get_next_available_id(self, model_cls, using=None):
# """
# Returns the next available id for the given model class.
# """
# all_ids = set(range(1, model_cls._default_manager.count()+1))
# used_ids = set(model_cls._default_manager.all().values_list('pk', flat=True))
# available_ids = all_ids - used_ids
# if available_ids:
# return min(available_ids)
# if used_ids:
# return max(used_ids) + 1
# return 1
# def get_default(self):
# """Returns the default value for this field"""
# return self.get_next_available_id(self.model)
# class SectionValidator:
# """Validation class for the `section` field on the `member` model."""
# def __init__(self, max_value="ZZZ"):
# self.max_value = max_value.upper()
# self.alphabet_size = ord("Z") - ord("A") + 1
# def is_valid(self, section: str, team_sections: list[str]=None) -> bool:
# """Returns boolean if the section passed is valid."""
# section = section.upper()
# if not self._is_alphanumeric(section):
# return False
# if not self._is_in_alphabet(section[0]):
# return False
# if not self._is_in_range(section):
# return False
# if team_sections:
# if not self._is_unique(section, team_sections):
# return False
# if not self._is_not_adjacent(section, team_sections):
# return False
# return True
# def _is_alphanumeric(self, section: str) -> bool:
# """Returns boolean if all characters in the passed string are alphanumerical."""
# return all(c.isalnum() for c in section)
# def _is_in_alphabet(self, c) -> bool:
# """Returns boolean if the passed character is alphabetical."""
# return "A" <= c <= "Z"
# def _is_in_range(self, section) -> bool:
# """Returns boolean if the passed section less or equal to the max value."""
# section_value = self._section_value(section)
# max_value = self._section_value(self.max_value)
# return section_value <= max_value
# def _is_unique(self, section, team_sections) -> bool:
# """Returns boolean if the passed section is unique amongst `team_sections`."""
# return section not in team_sections
# def _is_not_adjacent(self, section, team_sections) -> bool:
# """Returns boolean if the passed section is not adjacent to any `team_sections`."""
# for team_section in team_sections:
# team_section_value = self._section_value(team_section)
# section_value = self._section_value(section)
# if abs(team_section_value - section_value) <= 1:
# return False
# return True
# def _section_value(self, section):
# """Returns the value of the passed section."""
# n = len(section)
# value = sum((ord(c) - ord("A") + 1) * self.alphabet_size ** (n - i - 1) for i, c in enumerate(section))
# return value
# class SectionManager(models.Manager):
# @staticmethod
# def get_max_section():
# max_section = None
# max_number = -1
# # Iterate through all sections in the database
# for section in Section.objects.all():
# section_name = section.name
# section_number = 0
# # Calculate the section number based on the section name
# for i, char in enumerate(section_name):
# section_number += (ord(char) - ord('A') + 1) * (26 ** (len(section_name) - i - 1))
# # Check if this section has a higher number than the current maximum
# if section_number > max_number:
# max_number = section_number
# max_section = section_name
# return max_section
# @staticmethod
# def find_next_section(current_section):
# if not current_section:
# return 'A'
# # Split current section name into a list of characters
# chars = list(current_section)
# # Increment the last character
# chars[-1] = chr(ord(chars[-1]) + 1)
# # Check if the last character is "Z", and carry over to the next character if necessary
# for i in range(len(chars) - 1, -1, -1):
# if chars[i] > 'Z':
# chars[i] = 'A'
# if i == 0:
# # If the first character needs to be incremented, add a new character "A"
# chars.insert(0, 'A')
# else:
# # Increment the previous character
# chars[i - 1] = chr(ord(chars[i - 1]) + 1)
# else:
# break
# # Join the characters back into a string and return the result
# return ''.join(chars)
# class Section(models.Model):
# """Represents a fishing area. Members can be assigned to a section,
# but no 2 teammates can be in the same or adjacent section."""
# character = models.CharField(max_length=3, unique=True, null=False)
# objects = SectionManager()
# def clean(self):
# super().clean()
# self.character = self.character.upper()
# def __str__(self) -> str:
# return self.character
# class Member(models.Model):
# """Represents a member of a team"""
# first_name = models.CharField(max_length=255)
# last_name = models.CharField(max_length=255)
# team = models.ForeignKey("Team", on_delete=models.SET_NULL, null=True, blank=True, related_name='members')
# peg_number = models.PositiveIntegerField(null=True, editable=True, unique=True)
# section = models.ForeignKey(to=Section, on_delete=models.SET_NULL, null=True, swappable=True, related_name='members')
# def __init__(self, *args, **kwargs):
# super().__init__(*args, **kwargs)
# # If the peg_number field is not set, we assign it the smallest
# # available positive integer, excluding any used values.
# if not self.peg_number:
# used_peg_numbers = Member.objects.exclude(id=self.id).exclude(peg_number=None).values_list("peg_number", flat=True)
# peg_numbers = set(range(1, Member.objects.count() + 1)) - set(used_peg_numbers)
# if peg_numbers:
# self.peg_number = min (peg_numbers)
# def __str__(self):
# return f"{self.first_name} {self.last_name} (team {self.team.number}) [section {self.section.character}]"
# @property
# def fullname(self) -> str:
# return f"{self.first_name} {self.last_name}"
# class Team(models.Model):
# """Represents a team"""
# number = models.PositiveIntegerField(unique=True, null=False, blank=False)
# def __str__(self):
# return f"Team {self.number}"