Files
hoopscout-v3/backend/scouting/models.py
T
2026-06-03 21:37:15 +02:00

153 lines
6.2 KiB
Python

from django.conf import settings
from django.db import models
class UserProfile(models.Model):
ROLE_ADMIN = "admin"
ROLE_SCOUT = "scout"
ROLE_VIEWER = "viewer"
ROLE_CHOICES = [
(ROLE_ADMIN, "Admin"),
(ROLE_SCOUT, "Scout"),
(ROLE_VIEWER, "Viewer"),
]
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="profile")
role = models.CharField(max_length=16, choices=ROLE_CHOICES, default=ROLE_VIEWER)
def __str__(self) -> str:
return f"{self.user.username} ({self.role})"
class League(models.Model):
name = models.CharField(max_length=120)
code = models.CharField(max_length=16, unique=True)
region = models.CharField(max_length=60)
country = models.CharField(max_length=80)
class Meta:
ordering = ["region", "country", "name"]
def __str__(self) -> str:
return self.code
class Team(models.Model):
name = models.CharField(max_length=120)
league = models.ForeignKey(League, on_delete=models.PROTECT, related_name="teams")
country = models.CharField(max_length=80)
class Meta:
ordering = ["name"]
constraints = [models.UniqueConstraint(fields=["name", "league"], name="unique_team_per_league")]
def __str__(self) -> str:
return self.name
class Season(models.Model):
label = models.CharField(max_length=20, unique=True)
is_active = models.BooleanField(default=False)
class Meta:
ordering = ["-label"]
def __str__(self) -> str:
return self.label
class Player(models.Model):
POSITION_CHOICES = [
("PG", "Point Guard"),
("SG", "Shooting Guard"),
("SF", "Small Forward"),
("PF", "Power Forward"),
("C", "Center"),
]
first_name = models.CharField(max_length=80)
last_name = models.CharField(max_length=80)
position = models.CharField(max_length=2, choices=POSITION_CHOICES)
role = models.CharField(max_length=120, blank=True)
birth_year = models.PositiveSmallIntegerField(null=True, blank=True)
height_cm = models.PositiveSmallIntegerField(null=True, blank=True)
weight_kg = models.PositiveSmallIntegerField(null=True, blank=True)
nationality = models.CharField(max_length=80, blank=True)
current_team = models.ForeignKey(Team, on_delete=models.SET_NULL, null=True, blank=True, related_name="players")
external_source = models.CharField(max_length=80, blank=True)
external_id = models.CharField(max_length=120, blank=True)
profile_url = models.URLField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ["last_name", "first_name"]
constraints = [
models.UniqueConstraint(
fields=["first_name", "last_name", "birth_year", "nationality"],
name="unique_player_identity",
)
]
@property
def name(self) -> str:
return f"{self.first_name} {self.last_name}"
def __str__(self) -> str:
return self.name
class PlayerSeasonStat(models.Model):
player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="season_stats")
team = models.ForeignKey(Team, on_delete=models.PROTECT, related_name="season_stats")
league = models.ForeignKey(League, on_delete=models.PROTECT, related_name="season_stats")
season = models.ForeignKey(Season, on_delete=models.PROTECT, related_name="player_stats")
games_played = models.PositiveSmallIntegerField(default=0)
minutes_per_game = models.DecimalField(max_digits=5, decimal_places=2, default=0)
points_per_game = models.DecimalField(max_digits=5, decimal_places=2, default=0)
assists_per_game = models.DecimalField(max_digits=5, decimal_places=2, default=0)
rebounds_per_game = models.DecimalField(max_digits=5, decimal_places=2, default=0)
steals_per_game = models.DecimalField(max_digits=5, decimal_places=2, default=0)
blocks_per_game = models.DecimalField(max_digits=5, decimal_places=2, default=0)
turnovers_per_game = models.DecimalField(max_digits=5, decimal_places=2, default=0)
field_goal_percentage = models.DecimalField(max_digits=5, decimal_places=2, default=0)
three_point_percentage = models.DecimalField(max_digits=5, decimal_places=2, default=0)
free_throw_percentage = models.DecimalField(max_digits=5, decimal_places=2, default=0)
efficiency_rating = models.DecimalField(max_digits=6, decimal_places=2, default=0)
true_shooting_percentage = models.DecimalField(max_digits=5, decimal_places=2, default=0)
usage_percentage = models.DecimalField(max_digits=5, decimal_places=2, default=0)
total_points = models.PositiveSmallIntegerField(default=0)
total_assists = models.PositiveSmallIntegerField(default=0)
total_rebounds = models.PositiveSmallIntegerField(default=0)
class Meta:
ordering = ["-efficiency_rating", "-points_per_game"]
constraints = [
models.UniqueConstraint(fields=["player", "team", "league", "season"], name="unique_player_stat_line")
]
def __str__(self) -> str:
return f"{self.player} {self.season} {self.league}"
class PlayerGameLog(models.Model):
player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="game_logs")
team = models.ForeignKey(Team, on_delete=models.PROTECT, related_name="game_logs")
opponent = models.CharField(max_length=120)
league = models.ForeignKey(League, on_delete=models.PROTECT, related_name="game_logs")
season = models.ForeignKey(Season, on_delete=models.PROTECT, related_name="game_logs")
game_date = models.DateField()
points = models.PositiveSmallIntegerField(default=0)
assists = models.PositiveSmallIntegerField(default=0)
rebounds = models.PositiveSmallIntegerField(default=0)
steals = models.PositiveSmallIntegerField(default=0)
blocks = models.PositiveSmallIntegerField(default=0)
turnovers = models.PositiveSmallIntegerField(default=0)
efficiency_rating = models.DecimalField(max_digits=6, decimal_places=2, default=0)
class Meta:
ordering = ["-game_date"]
def __str__(self) -> str:
return f"{self.player} vs {self.opponent} on {self.game_date}"