From fec05e32b2c0113a95049efab550f3db9b1ef6f0 Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 4 Jun 2024 15:59:04 +0200 Subject: [PATCH] Splits the API and the site between two directories --- {tournaments => api}/permissions.py | 4 +- {tournaments => api}/serializers.py | 13 +- {tournaments => api}/tokens.py | 1 - api/urls.py | 33 ++++++ api/views.py | 170 +++++++++++++++++++++++++++ padelclub_backend/urls.py | 31 +---- tournaments/admin.py | 2 +- tournaments/signals.py | 1 - tournaments/views.py | 176 +--------------------------- 9 files changed, 214 insertions(+), 217 deletions(-) rename {tournaments => api}/permissions.py (79%) rename {tournaments => api}/serializers.py (92%) rename {tournaments => api}/tokens.py (90%) create mode 100644 api/urls.py create mode 100644 api/views.py diff --git a/tournaments/permissions.py b/api/permissions.py similarity index 79% rename from tournaments/permissions.py rename to api/permissions.py index 96c917e..582341a 100644 --- a/tournaments/permissions.py +++ b/api/permissions.py @@ -5,7 +5,7 @@ class IsClubOwner(permissions.BasePermission): def has_object_permission(self, request, view, club): # Check if the request user is the owner of the club - print(club.creator.id) - print(request.user.id) + # print(club.creator.id) + # print(request.user.id) return club.creator == request.user diff --git a/tournaments/serializers.py b/api/serializers.py similarity index 92% rename from tournaments/serializers.py rename to api/serializers.py index 107e8df..54a975f 100644 --- a/tournaments/serializers.py +++ b/api/serializers.py @@ -1,10 +1,9 @@ from rest_framework import serializers from tournaments.models.court import Court -from .models import Club, LiveMatch, TeamScore, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamRegistration, PlayerRegistration, Purchase, FailedApiCall, DateInterval +from tournaments.models import Club, LiveMatch, TeamScore, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamRegistration, PlayerRegistration, Purchase, FailedApiCall, DateInterval from django.contrib.auth import password_validation from django.utils.translation import gettext_lazy as _ # email -from .tokens import account_activation_token from django.template.loader import render_to_string from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode from django.utils.encoding import force_bytes @@ -59,13 +58,6 @@ class UserSerializer(serializers.ModelSerializer): return user - # def create(self, validated_data): - # user = CustomUser.objects.create_user( - # username=validated_data['username'], - # password=validated_data['password'], - # ) - # return user - def send_email(self, request, user): current_site = get_current_site(request) @@ -96,9 +88,6 @@ class ClubSerializer(serializers.ModelSerializer): class TournamentSerializer(serializers.ModelSerializer): class Meta: - # club_id = serializers.PrimaryKeyRelatedField(queryset=Club.objects.all()) - # event_id = serializers.PrimaryKeyRelatedField(queryset=Event.objects.all()) - # creator_id = serializers.PrimaryKeyRelatedField(queryset=CustomUser.objects.all()) model = Tournament fields = '__all__' # ['id', 'name', 'event', 'creator_id', 'start_date', 'end_date', 'creation_date', diff --git a/tournaments/tokens.py b/api/tokens.py similarity index 90% rename from tournaments/tokens.py rename to api/tokens.py index a656484..bfc65db 100644 --- a/tournaments/tokens.py +++ b/api/tokens.py @@ -1,5 +1,4 @@ from django.contrib.auth.tokens import PasswordResetTokenGenerator -# from django.utils import six class TokenGenerator(PasswordResetTokenGenerator): def _make_hash_value(self, user, timestamp): diff --git a/api/urls.py b/api/urls.py new file mode 100644 index 0000000..5564292 --- /dev/null +++ b/api/urls.py @@ -0,0 +1,33 @@ +from django.urls import include, path +from rest_framework import routers +from rest_framework.authtoken.views import obtain_auth_token + +from . import views + +router = routers.DefaultRouter() +router.register(r'users', views.UserViewSet) +router.register(r'clubs', views.ClubViewSet) +router.register(r'tournaments', views.TournamentViewSet) +router.register(r'events', views.EventViewSet) +router.register(r'rounds', views.RoundViewSet) +router.register(r'group-stages', views.GroupStageViewSet) +router.register(r'matches', views.MatchViewSet) +router.register(r'team-scores', views.TeamScoreViewSet) +router.register(r'team-registrations', views.TeamRegistrationViewSet) +router.register(r'player-registrations', views.PlayerRegistrationViewSet) +router.register(r'purchases', views.PurchaseViewSet) +router.register(r'courts', views.CourtViewSet) +router.register(r'date-intervals', views.DateIntervalViewSet) +router.register(r'failed-api-calls', views.FailedApiCallViewSet) + +urlpatterns = [ + path('', include(router.urls)), + + path('api-token-auth/', obtain_auth_token, name='api_token_auth'), + path("user-by-token/", views.user_by_token, name="user_by_token"), + path("change-password/", views.ChangePasswordView.as_view(), name="change_password"), + + # forgotten password + path('dj-rest-auth/', include('dj_rest_auth.urls')), + +] diff --git a/api/views.py b/api/views.py new file mode 100644 index 0000000..138f710 --- /dev/null +++ b/api/views.py @@ -0,0 +1,170 @@ +from .serializers import ClubSerializer, CourtSerializer, DateIntervalSerializer, TournamentSerializer, UserSerializer, ChangePasswordSerializer, EventSerializer, RoundSerializer, GroupStageSerializer, MatchSerializer, TeamScoreSerializer, TeamRegistrationSerializer, PlayerRegistrationSerializer, LiveMatchSerializer, PurchaseSerializer, UserUpdateSerializer, FailedApiCallSerializer +from tournaments.models import Club, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamScore, TeamRegistration, PlayerRegistration, Court, DateInterval, Purchase, FailedApiCall + +from rest_framework import viewsets, permissions +from rest_framework.authtoken.models import Token +from rest_framework.response import Response +from rest_framework.decorators import api_view +from rest_framework import status +from rest_framework.generics import UpdateAPIView +from rest_framework.exceptions import MethodNotAllowed +from rest_framework.permissions import IsAuthenticated + +from django.db.models import Q + +from .permissions import IsClubOwner + +@api_view(['GET']) +def user_by_token(request): + serializer = UserSerializer(request.user) + return Response(serializer.data, status=status.HTTP_200_OK) + +class UserViewSet(viewsets.ModelViewSet): + queryset = CustomUser.objects.all() + serializer_class = UserUpdateSerializer + permission_classes = [] # Users are public whereas the other requests are only for logged users + + def get_serializer_class(self): + # Use UserSerializer for other actions (e.g., create, retrieve) + if self.action in ['create', 'retrieve']: + return UserSerializer + return self.serializer_class + +class ClubViewSet(viewsets.ModelViewSet): + queryset = Club.objects.all() + serializer_class = ClubSerializer + permission_classes = [IsClubOwner] # Clubs are public whereas the other requests are only for logged users + + def perform_create(self, serializer): + serializer.save(creator=self.request.user) + +class TournamentViewSet(viewsets.ModelViewSet): + queryset = Tournament.objects.all() + serializer_class = TournamentSerializer + + def get_queryset(self): + if self.request.user.is_anonymous: + return [] + return self.queryset.filter(event__creator=self.request.user) + +class PurchaseViewSet(viewsets.ModelViewSet): + queryset = Purchase.objects.all() + serializer_class = PurchaseSerializer + + def get_queryset(self): + if self.request.user: + return self.queryset.filter(user=self.request.user) + return [] + + def put(self, request, pk): + raise MethodNotAllowed('PUT') + + def patch(self, request, pk): + raise MethodNotAllowed('PATCH') + + def delete(self, request, pk): + raise MethodNotAllowed('DELETE') + +# class ExpandedTournamentViewSet(viewsets.ModelViewSet): +# queryset = Tournament.objects.all() +# serializer_class = ExpandedTournamentSerializer + +class ChangePasswordView(UpdateAPIView): + serializer_class = ChangePasswordSerializer + + def update(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + user = serializer.save() + # if using drf authtoken, create a new token + if hasattr(user, 'auth_token'): + user.auth_token.delete() + token, created = Token.objects.get_or_create(user=user) + # return new token + return Response({'token': token.key}, status=status.HTTP_200_OK) + +class EventViewSet(viewsets.ModelViewSet): + queryset = Event.objects.all() + serializer_class = EventSerializer + + def get_queryset(self): + if self.request.user.is_anonymous: + return [] + return self.queryset.filter(creator=self.request.user) + +class RoundViewSet(viewsets.ModelViewSet): + queryset = Round.objects.all() + serializer_class = RoundSerializer + + def get_queryset(self): + if self.request.user: + return self.queryset.filter(tournament__event__creator=self.request.user) + return [] + +class GroupStageViewSet(viewsets.ModelViewSet): + queryset = GroupStage.objects.all() + serializer_class = GroupStageSerializer + + def get_queryset(self): + if self.request.user: + return self.queryset.filter(tournament__event__creator=self.request.user) + return [] + +class MatchViewSet(viewsets.ModelViewSet): + queryset = Match.objects.all() + serializer_class = MatchSerializer + + def get_queryset(self): + if self.request.user: + return self.queryset.filter(Q(group_stage__tournament__event__creator=self.request.user) | Q(round__tournament__event__creator=self.request.user)) + return [] + +class TeamScoreViewSet(viewsets.ModelViewSet): + queryset = TeamScore.objects.all() + serializer_class = TeamScoreSerializer + + def get_queryset(self): + if self.request.user: + return self.queryset.filter(team_registration__tournament__event__creator=self.request.user) + return [] + +class TeamRegistrationViewSet(viewsets.ModelViewSet): + queryset = TeamRegistration.objects.all() + serializer_class = TeamRegistrationSerializer + + def get_queryset(self): + if self.request.user: + return self.queryset.filter(tournament__event__creator=self.request.user) + return [] + +class PlayerRegistrationViewSet(viewsets.ModelViewSet): + queryset = PlayerRegistration.objects.all() + serializer_class = PlayerRegistrationSerializer + + def get_queryset(self): + if self.request.user: + return self.queryset.filter(team_registration__tournament__event__creator=self.request.user) + return [] + +class CourtViewSet(viewsets.ModelViewSet): + queryset = Court.objects.all() + serializer_class = CourtSerializer + +class DateIntervalViewSet(viewsets.ModelViewSet): + queryset = DateInterval.objects.all() + serializer_class = DateIntervalSerializer + + def get_queryset(self): + if self.request.user: + return self.queryset.filter(event__creator=self.request.user) + return [] + +class FailedApiCallViewSet(viewsets.ModelViewSet): + queryset = FailedApiCall.objects.all() + serializer_class = FailedApiCallSerializer + + def get_queryset(self): + return [] + + def perform_create(self, serializer): + serializer.save(user=self.request.user) diff --git a/padelclub_backend/urls.py b/padelclub_backend/urls.py index 1efb57e..660b4bb 100644 --- a/padelclub_backend/urls.py +++ b/padelclub_backend/urls.py @@ -14,41 +14,16 @@ Including another URLconf 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin -from django.urls import include, path, re_path -from rest_framework import routers -from tournaments import views -from rest_framework.authtoken.views import obtain_auth_token -from django.views.generic import TemplateView - -router = routers.DefaultRouter() -router.register(r'users', views.UserViewSet) -router.register(r'clubs', views.ClubViewSet) -router.register(r'tournaments', views.TournamentViewSet) -router.register(r'events', views.EventViewSet) -router.register(r'rounds', views.RoundViewSet) -router.register(r'group-stages', views.GroupStageViewSet) -router.register(r'matches', views.MatchViewSet) -router.register(r'team-scores', views.TeamScoreViewSet) -router.register(r'team-registrations', views.TeamRegistrationViewSet) -router.register(r'player-registrations', views.PlayerRegistrationViewSet) -router.register(r'purchases', views.PurchaseViewSet) -router.register(r'courts', views.CourtViewSet) -router.register(r'date-intervals', views.DateIntervalViewSet) -router.register(r'failed-api-calls', views.FailedApiCallViewSet) +from django.urls import include, path urlpatterns = [ - path('roads/', include(router.urls)), + # path('roads/', include(router.urls)), path("", include("tournaments.urls")), + path('roads/', include("api.urls")), path('kingdom/', admin.site.urls), path('api-auth/', include('rest_framework.urls')), - path('roads/api-token-auth/', obtain_auth_token, name='api_token_auth'), - path("roads/user-by-token/", views.user_by_token, name="user_by_token"), - path("roads/change-password/", views.ChangePasswordView.as_view(), name="change_password"), - - # forgotten password - path('api/dj-rest-auth/', include('dj_rest_auth.urls')), path('dj-auth/', include('django.contrib.auth.urls')), ] diff --git a/tournaments/admin.py b/tournaments/admin.py index 6e63501..95a57ac 100644 --- a/tournaments/admin.py +++ b/tournaments/admin.py @@ -54,7 +54,7 @@ class EventAdmin(admin.ModelAdmin): list_display = ['creation_date', 'name', 'club', 'creator'] class ClubAdmin(admin.ModelAdmin): - list_display = ['name', 'acronym', 'creator', 'events_count'] + list_display = ['name', 'acronym', 'city', 'creator', 'events_count', 'broadcast_code'] class PurchaseAdmin(admin.ModelAdmin): list_display = ['user', 'identifier', 'product_id', 'quantity', 'purchase_date', 'revocation_date'] diff --git a/tournaments/signals.py b/tournaments/signals.py index cb3eb63..7b4f604 100644 --- a/tournaments/signals.py +++ b/tournaments/signals.py @@ -8,7 +8,6 @@ def generate_unique_code(): characters = string.ascii_letters + string.digits while True: code = ''.join(random.sample(characters, 3)) - print(code) if not Club.objects.filter(broadcast_code=code).exists(): return code diff --git a/tournaments/views.py b/tournaments/views.py index ac2c077..fcb7c75 100644 --- a/tournaments/views.py +++ b/tournaments/views.py @@ -3,32 +3,20 @@ from django.http import HttpResponse from django.utils.encoding import force_str from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode from django.urls import reverse -from tournaments.models.court import Court -from tournaments.models.date_interval import DateInterval -from .tokens import account_activation_token - -from tournaments.models import group_stage -from .serializers import ClubSerializer, CourtSerializer, DateIntervalSerializer, TournamentSerializer, UserSerializer, ChangePasswordSerializer, EventSerializer, RoundSerializer, GroupStageSerializer, MatchSerializer, TeamScoreSerializer, TeamRegistrationSerializer, PlayerRegistrationSerializer, LiveMatchSerializer, PurchaseSerializer, UserUpdateSerializer, FailedApiCallSerializer -from .models import Club, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamScore, TeamRegistration, PlayerRegistration, Purchase, FailedApiCall +# from tournaments.models import group_stage +from .models import Court, DateInterval, Club, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamScore, TeamRegistration, PlayerRegistration, Purchase, FailedApiCall from .models import TeamSummon from datetime import datetime, timedelta -from rest_framework import viewsets, permissions -from rest_framework.authtoken.models import Token -from rest_framework.response import Response -from rest_framework.decorators import api_view -from rest_framework import status -from rest_framework.generics import UpdateAPIView -from rest_framework.exceptions import MethodNotAllowed -from rest_framework.permissions import IsAuthenticated -from .permissions import IsClubOwner from django.template import loader from datetime import date from django.http import JsonResponse from django.db.models import Q import json +from api.tokens import account_activation_token + from qr_code.qrcode.utils import QRCodeOptions def index(request): @@ -246,159 +234,3 @@ def activate(request, uidb64, token): def club_broadcast(request, broadcast_code): club = get_object_or_404(Club, broadcast_code=broadcast_code) return HttpResponse(club.name) - - -##### API ##### - -@api_view(['GET']) -def user_by_token(request): - # return Response({"message": "Hello for today! See you tomorrow!"}) - # key = request.data['token'] - # token = Token.objects.get(key=key) - # user = CustomUser.objects.get(username=token.user) - serializer = UserSerializer(request.user) - return Response(serializer.data, status=status.HTTP_200_OK) - -class UserViewSet(viewsets.ModelViewSet): - queryset = CustomUser.objects.all() - serializer_class = UserUpdateSerializer - permission_classes = [] # Users are public whereas the other requests are only for logged users - - def get_serializer_class(self): - # Use UserSerializer for other actions (e.g., create, retrieve) - if self.action in ['create', 'retrieve']: - return UserSerializer - return self.serializer_class - -class ClubViewSet(viewsets.ModelViewSet): - queryset = Club.objects.all() - serializer_class = ClubSerializer - permission_classes = [IsClubOwner] # Clubs are public whereas the other requests are only for logged users - -class TournamentViewSet(viewsets.ModelViewSet): - queryset = Tournament.objects.all() - serializer_class = TournamentSerializer - - def get_queryset(self): - if self.request.user.is_anonymous: - return [] - return self.queryset.filter(event__creator=self.request.user) - -class PurchaseViewSet(viewsets.ModelViewSet): - queryset = Purchase.objects.all() - serializer_class = PurchaseSerializer - - def get_queryset(self): - if self.request.user: - return self.queryset.filter(user=self.request.user) - return [] - - def put(self, request, pk): - raise MethodNotAllowed('PUT') - - def patch(self, request, pk): - raise MethodNotAllowed('PATCH') - - def delete(self, request, pk): - raise MethodNotAllowed('DELETE') - -# class ExpandedTournamentViewSet(viewsets.ModelViewSet): -# queryset = Tournament.objects.all() -# serializer_class = ExpandedTournamentSerializer - -class ChangePasswordView(UpdateAPIView): - serializer_class = ChangePasswordSerializer - - def update(self, request, *args, **kwargs): - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - user = serializer.save() - # if using drf authtoken, create a new token - if hasattr(user, 'auth_token'): - user.auth_token.delete() - token, created = Token.objects.get_or_create(user=user) - # return new token - return Response({'token': token.key}, status=status.HTTP_200_OK) - -class EventViewSet(viewsets.ModelViewSet): - queryset = Event.objects.all() - serializer_class = EventSerializer - - def get_queryset(self): - if self.request.user.is_anonymous: - return [] - return self.queryset.filter(creator=self.request.user) - -class RoundViewSet(viewsets.ModelViewSet): - queryset = Round.objects.all() - serializer_class = RoundSerializer - - def get_queryset(self): - if self.request.user: - return self.queryset.filter(tournament__event__creator=self.request.user) - return [] - -class GroupStageViewSet(viewsets.ModelViewSet): - queryset = GroupStage.objects.all() - serializer_class = GroupStageSerializer - - def get_queryset(self): - if self.request.user: - return self.queryset.filter(tournament__event__creator=self.request.user) - return [] - -class MatchViewSet(viewsets.ModelViewSet): - queryset = Match.objects.all() - serializer_class = MatchSerializer - - def get_queryset(self): - if self.request.user: - return self.queryset.filter(Q(group_stage__tournament__event__creator=self.request.user) | Q(round__tournament__event__creator=self.request.user)) - return [] - -class TeamScoreViewSet(viewsets.ModelViewSet): - queryset = TeamScore.objects.all() - serializer_class = TeamScoreSerializer - - def get_queryset(self): - if self.request.user: - return self.queryset.filter(team_registration__tournament__event__creator=self.request.user) - return [] - -class TeamRegistrationViewSet(viewsets.ModelViewSet): - queryset = TeamRegistration.objects.all() - serializer_class = TeamRegistrationSerializer - - def get_queryset(self): - if self.request.user: - return self.queryset.filter(tournament__event__creator=self.request.user) - return [] - -class PlayerRegistrationViewSet(viewsets.ModelViewSet): - queryset = PlayerRegistration.objects.all() - serializer_class = PlayerRegistrationSerializer - - def get_queryset(self): - if self.request.user: - return self.queryset.filter(team_registration__tournament__event__creator=self.request.user) - return [] - -class CourtViewSet(viewsets.ModelViewSet): - queryset = Court.objects.all() - serializer_class = CourtSerializer - -class DateIntervalViewSet(viewsets.ModelViewSet): - queryset = DateInterval.objects.all() - serializer_class = DateIntervalSerializer - - def get_queryset(self): - if self.request.user: - return self.queryset.filter(event__creator=self.request.user) - return [] - -class FailedApiCallViewSet(viewsets.ModelViewSet): - queryset = FailedApiCall.objects.all() - serializer_class = FailedApiCallSerializer - - def get_queryset(self): - return []