from .serializers import ClubSerializer, CourtSerializer, DateIntervalSerializer, TournamentSerializer, UserSerializer, ChangePasswordSerializer, EventSerializer, RoundSerializer, GroupStageSerializer, MatchSerializer, TeamScoreSerializer, TeamRegistrationSerializer, PlayerRegistrationSerializer, LiveMatchSerializer, PurchaseSerializer, CustomUserSerializer, FailedApiCallSerializer, LogSerializer, DeviceTokenSerializer from tournaments.models import Club, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamScore, TeamRegistration, PlayerRegistration, Court, DateInterval, Purchase, FailedApiCall, Log, DeviceToken, ModelLog 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 rest_framework.views import APIView from django.contrib.auth import authenticate from django.db.models import Q from django.core.exceptions import ObjectDoesNotExist from django.utils import timezone from django.apps import apps from collections import defaultdict from .permissions import IsClubOwner from .utils import is_valid_email, build_serializer_class class DataApi(APIView): permission_classes = [IsAuthenticated] def post(self, request, *args, **kwargs): # unfold content model_operation = request.data.get('operation') model_name = request.data.get('model_name') data = request.data.get('data') print(f"/data {model_operation} {model_name}") serializer_class = build_serializer_class(model_name) model = apps.get_model(app_label='tournaments', model_name=model_name) try: instance = model.objects.get(id=data.get('id')) if model_operation == 'DELETE': return self.delete_and_save_log(request, data, model_operation, model_name) else: # POST/PUT serializer = serializer_class(instance, data=data, context={'request': request}) if serializer.is_valid(): if instance.last_update <= serializer.validated_data.get('last_update'): print('>>> update') return self.save_and_create_log(request, serializer, model_operation, model_name) else: print('>>> return 203') return Response(serializer.data, status=status.HTTP_203_NON_AUTHORITATIVE_INFORMATION) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) except model.DoesNotExist: # If the instance doesn't exist, we should be in a POST situation print('>>> insert') serializer = serializer_class(data=data, context={'request': request}) if serializer.is_valid(): return self.save_and_create_log(request, serializer, model_operation, model_name) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def save_and_create_log(self, request, serializer, model_operation, model_name): instance = serializer.save() self.create_and_save_model_log( user=request.user, model_operation=model_operation, model_name=model_name, model_id=instance.id ) return Response(serializer.data, status=status.HTTP_201_CREATED) def delete_and_save_log(self, request, data, model_operation, model_name): model = apps.get_model(app_label='tournaments', model_name=model_name) try: instance = model.objects.get(id=data.id) instance.delete() except model.DoesNotExist: return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND) # we delete all logs linked to the instance because they won't be needed anymore ModelLog.objects.filter(model_id=instance.id).delete() self.create_and_save_model_log( user=request.user, model_operation=model_operation, model_name=model_name, model_id=instance.id ) return Response(status=status.HTTP_204_NO_CONTENT) def create_and_save_model_log(self, user, model_operation, model_name, model_id): model_log = ModelLog() model_log.user = user model_log.operation = model_operation model_log.date = timezone.localtime(timezone.now()) model_log.model_name = model_name model_log.model_id = model_id model_log.save() def get(self, request, *args, **kwargs): print('/data GET YEAH!') last_update = request.query_params.get('last_update') if not last_update: return Response({"error": "last_update parameter is required"}, status=status.HTTP_400_BAD_REQUEST) print(last_update) try: last_update = timezone.datetime.fromisoformat(last_update) except ValueError: return Response({"error": f"Invalid date format for last_update: {last_update}"}, status=status.HTTP_400_BAD_REQUEST) logs = ModelLog.objects.filter(date__gt=last_update).order_by('date') updates = defaultdict(dict) deletions = defaultdict(set) for log in logs: model = apps.get_model(app_label='tournaments', model_name=log.model_name) if log.operation in ['POST', 'PUT']: try: instance = model.objects.get(id=log.model_id) serializer_class = build_serializer_class(log.model_name) serializer = serializer_class(instance) updates[log.model_name][log.model_id] = serializer.data except model.DoesNotExist: # If the instance doesn't exist, it might have been deleted after this log was created pass elif log.operation == 'DELETE': deletions[log.model_name].add(log.model_id) # Convert updates dict to list for each model for model_name in updates: updates[model_name] = list(updates[model_name].values()) # Convert deletions set to list for each model for model_name in deletions: deletions[model_name] = list(deletions[model_name]) # local_time = timezone.localtime(timezone.now()) # print(local_time.isoformat(timespec='seconds')) date = logs.last().date.astimezone().isoformat(timespec='seconds') if logs else None print(date) response_data = { "updates": dict(updates), "deletions": dict(deletions), "date": date } return Response(response_data, status=status.HTTP_200_OK) class CustomAuthToken(APIView): permission_classes = [] def post(self, request, *args, **kwargs): username = request.data.get('username') password = request.data.get('password') device_id = request.data.get('device_id') user = authenticate(username=username, password=password) if user is None and is_valid_email(username) == True: true_username = self.get_username_from_email(username) user = authenticate(username=true_username, password=password) if user is not None: # if user.device_id is None or user.device_id == device_id or user.username == 'apple-test': # if user.device_id is None or user.device_id == device_id or user.username == 'apple-test': user.device_id = device_id user.save() token, created = Token.objects.get_or_create(user=user) return Response({'token': token.key}) # else: # return Response({'error': 'Vous ne pouvez pour l\'instant vous connecter sur plusieurs appareils en même temps. Veuillez vous déconnecter du précédent appareil. Autrement, veuillez contacter le support.'}, status=status.HTTP_403_FORBIDDEN) else: return Response({'error': 'L\'utilisateur et le mot de passe de correspondent pas'}, status=status.HTTP_401_UNAUTHORIZED) def get_username_from_email(self, email): try: user = CustomUser.objects.get(email=email) return user.username except ObjectDoesNotExist: return None # or handle the case where the user doesn't exist class Logout(APIView): permission_classes = (IsAuthenticated,) def post(self, request, *args, **kwargs): request.user.auth_token.delete() device_id = request.data.get('device_id') if request.user.device_id == device_id: request.user.device_id = None request.user.save() return Response(status=status.HTTP_200_OK) @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 = CustomUserSerializer 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): tournament_id = self.request.query_params.get('tournament') if tournament_id: return self.queryset.filter(tournament=tournament_id) 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): tournament_id = self.request.query_params.get('tournament') if tournament_id: return self.queryset.filter(tournament=tournament_id) 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): tournament_id = self.request.query_params.get('tournament') if tournament_id: return self.queryset.filter(Q(group_stage__tournament=tournament_id) | Q(round__tournament=tournament_id)) 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): tournament_id = self.request.query_params.get('tournament') if tournament_id: return self.queryset.filter(team_registration__tournament=tournament_id) 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): tournament_id = self.request.query_params.get('tournament') if tournament_id: return self.queryset.filter(tournament=tournament_id) 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): tournament_id = self.request.query_params.get('tournament') if tournament_id: return self.queryset.filter(team_registration__tournament=tournament_id) 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 permission_classes = [] # FailedApiCall are public whereas the other requests are only for logged users def get_queryset(self): return [] def perform_create(self, serializer): if self.request.user.is_anonymous == False: serializer.save(user=self.request.user) else: serializer.save() class LogViewSet(viewsets.ModelViewSet): queryset = Log.objects.all() serializer_class = LogSerializer permission_classes = [] # Log are public whereas the other requests are only for logged users def get_queryset(self): return [] def perform_create(self, serializer): if self.request.user.is_anonymous == False: serializer.save(user=self.request.user) else: serializer.save() class DeviceTokenViewSet(viewsets.ModelViewSet): queryset = DeviceToken.objects.all() serializer_class = DeviceTokenSerializer def get_queryset(self): if self.request.user: return self.queryset.filter(user=self.request.user) return [] def create(self, request, *args, **kwargs): value = request.data.get('value') if DeviceToken.objects.filter(value=value).exists(): return Response({"detail": "This device token is already registered."}, status=208) serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) def perform_create(self, serializer): serializer.save(user=self.request.user)