192 lines
6.0 KiB
Python
192 lines
6.0 KiB
Python
from django.db import models
|
|
|
|
|
|
class TimeStampedModel(models.Model):
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
abstract = True
|
|
|
|
|
|
class Nationality(TimeStampedModel):
|
|
name = models.CharField(max_length=120, unique=True)
|
|
iso2_code = models.CharField(max_length=2, unique=True)
|
|
iso3_code = models.CharField(max_length=3, unique=True, blank=True, null=True)
|
|
|
|
class Meta:
|
|
ordering = ["name"]
|
|
indexes = [models.Index(fields=["name"]), models.Index(fields=["iso2_code"])]
|
|
verbose_name_plural = "Nationalities"
|
|
|
|
def __str__(self) -> str:
|
|
return f"{self.name} ({self.iso2_code})"
|
|
|
|
|
|
class Position(TimeStampedModel):
|
|
code = models.CharField(max_length=10, unique=True)
|
|
name = models.CharField(max_length=80, unique=True)
|
|
|
|
class Meta:
|
|
ordering = ["code"]
|
|
indexes = [models.Index(fields=["code"]), models.Index(fields=["name"])]
|
|
|
|
def __str__(self) -> str:
|
|
return f"{self.code} - {self.name}"
|
|
|
|
|
|
class Role(TimeStampedModel):
|
|
code = models.CharField(max_length=32, unique=True)
|
|
name = models.CharField(max_length=120, unique=True)
|
|
description = models.TextField(blank=True)
|
|
|
|
class Meta:
|
|
ordering = ["name"]
|
|
indexes = [models.Index(fields=["code"]), models.Index(fields=["name"])]
|
|
|
|
def __str__(self) -> str:
|
|
return self.name
|
|
|
|
|
|
class Player(TimeStampedModel):
|
|
class DominantHand(models.TextChoices):
|
|
RIGHT = "right", "Right"
|
|
LEFT = "left", "Left"
|
|
BOTH = "both", "Both"
|
|
UNKNOWN = "unknown", "Unknown"
|
|
|
|
first_name = models.CharField(max_length=120)
|
|
last_name = models.CharField(max_length=120)
|
|
full_name = models.CharField(max_length=260)
|
|
birth_date = models.DateField(blank=True, null=True)
|
|
nationality = models.ForeignKey(
|
|
"players.Nationality",
|
|
on_delete=models.SET_NULL,
|
|
blank=True,
|
|
null=True,
|
|
related_name="players",
|
|
)
|
|
nominal_position = models.ForeignKey(
|
|
"players.Position",
|
|
on_delete=models.SET_NULL,
|
|
blank=True,
|
|
null=True,
|
|
related_name="nominal_players",
|
|
)
|
|
inferred_role = models.ForeignKey(
|
|
"players.Role",
|
|
on_delete=models.SET_NULL,
|
|
blank=True,
|
|
null=True,
|
|
related_name="role_players",
|
|
)
|
|
height_cm = models.PositiveSmallIntegerField(blank=True, null=True)
|
|
weight_kg = models.PositiveSmallIntegerField(blank=True, null=True)
|
|
wingspan_cm = models.PositiveSmallIntegerField(blank=True, null=True)
|
|
dominant_hand = models.CharField(
|
|
max_length=16,
|
|
choices=DominantHand.choices,
|
|
default=DominantHand.UNKNOWN,
|
|
)
|
|
is_active = models.BooleanField(default=True)
|
|
|
|
class Meta:
|
|
ordering = ["full_name", "id"]
|
|
constraints = [
|
|
models.UniqueConstraint(
|
|
fields=["full_name", "birth_date"],
|
|
name="uq_player_full_name_birth_date",
|
|
)
|
|
]
|
|
indexes = [
|
|
models.Index(fields=["full_name"]),
|
|
models.Index(fields=["last_name", "first_name"]),
|
|
models.Index(fields=["birth_date"]),
|
|
models.Index(fields=["nationality"]),
|
|
models.Index(fields=["nominal_position"]),
|
|
models.Index(fields=["inferred_role"]),
|
|
models.Index(fields=["is_active"]),
|
|
models.Index(fields=["height_cm"]),
|
|
]
|
|
|
|
def __str__(self) -> str:
|
|
return self.full_name
|
|
|
|
|
|
class PlayerAlias(TimeStampedModel):
|
|
player = models.ForeignKey("players.Player", on_delete=models.CASCADE, related_name="aliases")
|
|
alias = models.CharField(max_length=260)
|
|
source = models.CharField(max_length=80, blank=True)
|
|
is_primary = models.BooleanField(default=False)
|
|
|
|
class Meta:
|
|
ordering = ["alias"]
|
|
constraints = [
|
|
models.UniqueConstraint(fields=["player", "alias"], name="uq_player_alias_per_player")
|
|
]
|
|
indexes = [models.Index(fields=["alias"]), models.Index(fields=["source"])]
|
|
|
|
def __str__(self) -> str:
|
|
return f"{self.alias} ({self.player_id})"
|
|
|
|
|
|
class PlayerCareerEntry(TimeStampedModel):
|
|
player = models.ForeignKey(
|
|
"players.Player",
|
|
on_delete=models.CASCADE,
|
|
related_name="career_entries",
|
|
)
|
|
team = models.ForeignKey(
|
|
"teams.Team",
|
|
on_delete=models.SET_NULL,
|
|
blank=True,
|
|
null=True,
|
|
related_name="career_entries",
|
|
)
|
|
competition = models.ForeignKey(
|
|
"competitions.Competition",
|
|
on_delete=models.SET_NULL,
|
|
blank=True,
|
|
null=True,
|
|
related_name="career_entries",
|
|
)
|
|
season = models.ForeignKey(
|
|
"competitions.Season",
|
|
on_delete=models.SET_NULL,
|
|
blank=True,
|
|
null=True,
|
|
related_name="career_entries",
|
|
)
|
|
role_snapshot = models.ForeignKey(
|
|
"players.Role",
|
|
on_delete=models.SET_NULL,
|
|
blank=True,
|
|
null=True,
|
|
related_name="career_entries",
|
|
)
|
|
shirt_number = models.PositiveSmallIntegerField(blank=True, null=True)
|
|
start_date = models.DateField(blank=True, null=True)
|
|
end_date = models.DateField(blank=True, null=True)
|
|
notes = models.TextField(blank=True)
|
|
|
|
class Meta:
|
|
ordering = ["player", "-start_date", "-id"]
|
|
constraints = [
|
|
models.UniqueConstraint(
|
|
fields=["player", "team", "competition", "season", "start_date"],
|
|
name="uq_player_career_entry_scope",
|
|
),
|
|
models.CheckConstraint(
|
|
check=models.Q(end_date__isnull=True) | models.Q(start_date__isnull=True) | models.Q(end_date__gte=models.F("start_date")),
|
|
name="ck_career_entry_dates",
|
|
),
|
|
]
|
|
indexes = [
|
|
models.Index(fields=["player", "start_date"]),
|
|
models.Index(fields=["team", "season"]),
|
|
models.Index(fields=["competition", "season"]),
|
|
]
|
|
|
|
def __str__(self) -> str:
|
|
return f"{self.player} | {self.team or '-'} | {self.season or '-'}"
|