from rest_framework import generics from rest_framework import status from rest_framework.pagination import PageNumberPagination from rest_framework.response import Response from rest_framework.throttling import AnonRateThrottle, UserRateThrottle from apps.competitions.models import Competition, Season from apps.players.forms import PlayerSearchForm from apps.players.models import Player from apps.players.services.search import ( METRIC_SORT_KEYS, annotate_player_metrics, apply_sorting, base_player_queryset, filter_players, ) from apps.teams.models import Team from .permissions import ReadOnlyOrDeny from .serializers import ( CompetitionSerializer, PlayerDetailSerializer, PlayerListSerializer, SeasonSerializer, TeamSerializer, ) class ApiPagination(PageNumberPagination): page_size = 20 page_size_query_param = "page_size" max_page_size = 100 class ReadOnlyBaseAPIView: permission_classes = [ReadOnlyOrDeny] throttle_classes = [AnonRateThrottle, UserRateThrottle] class PlayerSearchApiView(ReadOnlyBaseAPIView, generics.ListAPIView): serializer_class = PlayerListSerializer pagination_class = ApiPagination def get_search_form(self): if not hasattr(self, "_search_form"): self._search_form = PlayerSearchForm(self.request.query_params) return self._search_form def _validation_error_response(self): form = self.get_search_form() return Response( { "detail": "Invalid search parameters.", "errors": form.errors.get_json_data(escape_html=True), }, status=status.HTTP_400_BAD_REQUEST, ) def list(self, request, *args, **kwargs): form = self.get_search_form() if form.is_bound and not form.is_valid(): return self._validation_error_response() return super().list(request, *args, **kwargs) def get_queryset(self): form = self.get_search_form() queryset = base_player_queryset() queryset = filter_players(queryset, form.cleaned_data) sort_key = form.cleaned_data.get("sort", "name_asc") if sort_key in METRIC_SORT_KEYS: queryset = annotate_player_metrics(queryset, form.cleaned_data) queryset = apply_sorting(queryset, sort_key) return queryset class PlayerDetailApiView(ReadOnlyBaseAPIView, generics.RetrieveAPIView): serializer_class = PlayerDetailSerializer queryset = Player.objects.select_related( "nationality", "nominal_position", "inferred_role", "origin_competition", "origin_team", ).prefetch_related("aliases") class CompetitionListApiView(ReadOnlyBaseAPIView, generics.ListAPIView): serializer_class = CompetitionSerializer queryset = Competition.objects.select_related("country").order_by("name") class TeamListApiView(ReadOnlyBaseAPIView, generics.ListAPIView): serializer_class = TeamSerializer queryset = Team.objects.select_related("country").order_by("name") class SeasonListApiView(ReadOnlyBaseAPIView, generics.ListAPIView): serializer_class = SeasonSerializer queryset = Season.objects.order_by("-start_date")