from django import forms from apps.competitions.models import Competition, Season from apps.players.models import Nationality, Position, Role from apps.teams.models import Team class PlayerSearchForm(forms.Form): SORT_CHOICES = ( ("name_asc", "Name (A-Z)"), ("name_desc", "Name (Z-A)"), ("age_youngest", "Age (Youngest first)"), ("age_oldest", "Age (Oldest first)"), ("height_desc", "Height (Tallest first)"), ("height_asc", "Height (Shortest first)"), ("ppg_desc", "Points per game (High to low)"), ("ppg_asc", "Points per game (Low to high)"), ("mpg_desc", "Minutes per game (High to low)"), ("mpg_asc", "Minutes per game (Low to high)"), ) PAGE_SIZE_CHOICES = ((20, "20"), (50, "50"), (100, "100")) q = forms.CharField(required=False, label="Name") nominal_position = forms.ModelChoiceField(queryset=Position.objects.none(), required=False) inferred_role = forms.ModelChoiceField(queryset=Role.objects.none(), required=False) competition = forms.ModelChoiceField(queryset=Competition.objects.none(), required=False) origin_competition = forms.ModelChoiceField(queryset=Competition.objects.none(), required=False) nationality = forms.ModelChoiceField(queryset=Nationality.objects.none(), required=False) team = forms.ModelChoiceField(queryset=Team.objects.none(), required=False) origin_team = forms.ModelChoiceField(queryset=Team.objects.none(), required=False) season = forms.ModelChoiceField(queryset=Season.objects.none(), required=False) age_min = forms.IntegerField(required=False, min_value=0, max_value=60, label="Min age") age_max = forms.IntegerField(required=False, min_value=0, max_value=60, label="Max age") height_min = forms.IntegerField(required=False, min_value=120, max_value=250, label="Min height (cm)") height_max = forms.IntegerField(required=False, min_value=120, max_value=250, label="Max height (cm)") weight_min = forms.IntegerField(required=False, min_value=40, max_value=200, label="Min weight (kg)") weight_max = forms.IntegerField(required=False, min_value=40, max_value=200, label="Max weight (kg)") games_played_min = forms.IntegerField(required=False, min_value=0) games_played_max = forms.IntegerField(required=False, min_value=0) minutes_per_game_min = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=6) minutes_per_game_max = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=6) points_per_game_min = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=6) points_per_game_max = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=6) rebounds_per_game_min = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=6) rebounds_per_game_max = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=6) assists_per_game_min = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=6) assists_per_game_max = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=6) steals_per_game_min = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=6) steals_per_game_max = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=6) blocks_per_game_min = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=6) blocks_per_game_max = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=6) turnovers_per_game_min = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=6) turnovers_per_game_max = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=6) fg_pct_min = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=5, label="FG% min") fg_pct_max = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=5, label="FG% max") three_pct_min = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=5, label="3P% min") three_pct_max = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=5, label="3P% max") ft_pct_min = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=5, label="FT% min") ft_pct_max = forms.DecimalField(required=False, min_value=0, decimal_places=2, max_digits=5, label="FT% max") efficiency_metric_min = forms.DecimalField( required=False, min_value=0, decimal_places=2, max_digits=6, label="Impact metric min", ) efficiency_metric_max = forms.DecimalField( required=False, min_value=0, decimal_places=2, max_digits=6, label="Impact metric max", ) sort = forms.ChoiceField(choices=SORT_CHOICES, required=False, initial="name_asc") page_size = forms.TypedChoiceField( choices=PAGE_SIZE_CHOICES, required=False, coerce=int, initial=20, ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["nominal_position"].queryset = Position.objects.order_by("code") self.fields["inferred_role"].queryset = Role.objects.order_by("name") self.fields["competition"].queryset = Competition.objects.order_by("name") self.fields["origin_competition"].queryset = Competition.objects.order_by("name") self.fields["nationality"].queryset = Nationality.objects.order_by("name") self.fields["team"].queryset = Team.objects.order_by("name") self.fields["origin_team"].queryset = Team.objects.order_by("name") self.fields["season"].queryset = Season.objects.order_by("-start_date") def clean(self): cleaned_data = super().clean() self._validate_min_max(cleaned_data, "age_min", "age_max") self._validate_min_max(cleaned_data, "height_min", "height_max") self._validate_min_max(cleaned_data, "weight_min", "weight_max") self._validate_min_max(cleaned_data, "games_played_min", "games_played_max") self._validate_min_max(cleaned_data, "minutes_per_game_min", "minutes_per_game_max") self._validate_min_max(cleaned_data, "points_per_game_min", "points_per_game_max") self._validate_min_max(cleaned_data, "rebounds_per_game_min", "rebounds_per_game_max") self._validate_min_max(cleaned_data, "assists_per_game_min", "assists_per_game_max") self._validate_min_max(cleaned_data, "steals_per_game_min", "steals_per_game_max") self._validate_min_max(cleaned_data, "blocks_per_game_min", "blocks_per_game_max") self._validate_min_max(cleaned_data, "turnovers_per_game_min", "turnovers_per_game_max") self._validate_min_max(cleaned_data, "fg_pct_min", "fg_pct_max") self._validate_min_max(cleaned_data, "three_pct_min", "three_pct_max") self._validate_min_max(cleaned_data, "ft_pct_min", "ft_pct_max") self._validate_min_max(cleaned_data, "efficiency_metric_min", "efficiency_metric_max") if not cleaned_data.get("sort"): cleaned_data["sort"] = "name_asc" if not cleaned_data.get("page_size"): cleaned_data["page_size"] = 20 return cleaned_data def _validate_min_max(self, cleaned_data: dict, minimum_key: str, maximum_key: str) -> None: minimum = cleaned_data.get(minimum_key) maximum = cleaned_data.get(maximum_key) if minimum is not None and maximum is not None and minimum > maximum: self.add_error(maximum_key, f"{maximum_key.replace('_', ' ')} must be >= {minimum_key.replace('_', ' ')}")