2020-11-29 04:44:35 +00:00

339 lines
15 KiB
Python

from django.shortcuts import render
from rest_framework import permissions, status, generics
from rest_framework.response import Response
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import User
from rest_framework.decorators import api_view, permission_classes
from django.contrib.sites.shortcuts import get_current_site
from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.template.loader import render_to_string
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.core.mail import EmailMultiAlternatives
from .serializers import UserRegisterSerializer, UserResetPasswordSerializer, UserPasswordSerializer, GameListSerializer, TeamSerializer, CurrentCheckPointSerializer, CheckPointCoordinatesSerializer, ProfileInfoSerializer
from django.utils.html import strip_tags
from django.contrib.auth.password_validation import validate_password
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter, SearchFilter
from .models import Game, Team, Gamer, Profile, InvitationToken, CurrentCheckPoint, CheckPoint
from django.conf import settings
from django.utils import timezone
from hashlib import sha1
import six
import threading
import math
class EmailTokenGenerator(PasswordResetTokenGenerator):
def _make_hash_value(self, user, timestamp):
return(six.text_type(user.pk) + six.text_type(timestamp) + six.text_type(user.is_active))
email_token_gen = EmailTokenGenerator()
password_token_gen = PasswordResetTokenGenerator()
def list_to_queryset(model_list):
if len(model_list) > 0:
return model_list[0].__class__.objects.filter(
pk__in=[obj.pk for obj in model_list])
else:
return []
class ListGamesAPIView(generics.ListAPIView):
serializer_class = GameListSerializer
permission_classes = [permissions.IsAuthenticated]
filter_backends = [SearchFilter, OrderingFilter, DjangoFilterBackend]
filterset_fields = ['co_op','max_gamers','active','average_time']
ordering_fields = ['max_gamers','active','average_time','name']
search_fields = ['name', 'description']
def get_queryset(self):
profile = Profile.objects.get(user = self.request.user)
data = []
for game_i in Game.objects.filter(active = True):
teams = game_i.teams.all()
if(self.kwargs['visited'] == '0'):
test = True
for team_i in teams:
gamer_i = Gamer.objects.filter(team = team_i, profile = profile)
if(gamer_i.exists()):
test = False
break
if(test):
data.append(game_i)
else:
test = False
for team_i in teams:
gamer_i = Gamer.objects.filter(team = team_i, profile = profile)
if(gamer_i.exists()):
test = True
break
if(test):
data.append(game_i)
if(len(data)!=0):
return list_to_queryset(data)
else:
return Game.objects.none()
@api_view(['GET'])
@permission_classes([permissions.IsAuthenticated])
def SingleGameAPIView(request, slug):
game = Game.objects.filter(slug = slug)
if(game.exists()):
profile = Profile.objects.get(user = request.user)
game = game.first()
teams = Team.objects.filter(game = game)
team = []
for team_i in teams:
if(Gamer.objects.filter(profile = profile, team = team_i).exists()):
team = team_i
break
if(team):
serializer = TeamSerializer(team)
data = serializer.data
gamers = data['gamers']
can_start = False
invite_token = ''
if(not team.active):
if(team.captain == profile and (not game.co_op or len(gamers) == game.max_gamers) and not team.finished):
can_start = True
if(team.captain == profile and len(gamers) != game.max_gamers and not team.finished):
invite = InvitationToken.objects.filter(team = team)
if(invite.exists()):
invite = invite.first()
invite_token = invite.token
if(team.finished):
done_chepoints = []
cur_checkpoint = CurrentCheckPoint.objects.get(team = team).check_point
temp_check_point = CheckPoint.objects.get(game = team.game, start = True)
done_chepoints.append(CheckPointCoordinatesSerializer(temp_check_point).data)
while temp_check_point.id!=cur_checkpoint.id:
temp_check_point = temp_check_point.next_checkpoint
done_chepoints.append(CheckPointCoordinatesSerializer(temp_check_point).data)
return Response({'ok':True, 'can_take_part':False, 'can_start':can_start, 'invite_token':invite_token, **data, 'done_chepoints':done_chepoints}, status = status.HTTP_200_OK)
return Response({'ok':True, 'can_take_part':False, 'can_start':can_start, 'invite_token':invite_token, **data}, status = status.HTTP_200_OK)
else:
done_chepoints = []
cur_checkpoint = CurrentCheckPoint.objects.get(team = team).check_point
cur_checkpoint_serializer = CurrentCheckPointSerializer(cur_checkpoint)
temp_check_point = CheckPoint.objects.get(game = team.game, start = True)
done_chepoints.append(CheckPointCoordinatesSerializer(temp_check_point).data)
while temp_check_point.id!=cur_checkpoint.id:
temp_check_point = temp_check_point.next_checkpoint
done_chepoints.append(CheckPointCoordinatesSerializer(temp_check_point).data)
return Response({'ok':True, 'can_take_part':False, 'can_start':can_start, 'invite_token':invite_token, **data, 'current_checkpoint':cur_checkpoint_serializer.data, 'done_chepoints':done_chepoints}, status = status.HTTP_200_OK)
else:
serializer = GameListSerializer(game)
data = serializer.data
return Response({'ok':True, 'can_take_part':True, **serializer.data}, status = status.HTTP_200_OK)
return Response({'ok':False, 'error':'Game does not exist'}, status = status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def CloseCheckPointAPIView(request, slug):
game = Game.objects.filter(slug = slug)
if(game.exists()):
profile = Profile.objects.get(user = request.user)
game = game.first()
teams = Team.objects.filter(game = game)
team = []
for team_i in teams:
if(Gamer.objects.filter(profile = profile, team = team_i).exists()):
team = team_i
break
if(team):
temp_check_point = CheckPointCoordinatesSerializer(data = request.data)
if(temp_check_point.is_valid() and team.active and not team.finished):
temp_check_point = temp_check_point.validated_data
cur_checkpoint = CurrentCheckPoint.objects.get(team = team)
dest = math.sqrt((temp_check_point['coordinates_lat']-cur_checkpoint.check_point.next_checkpoint.coordinates_lat)**2 + (temp_check_point['coordinates_lon']-cur_checkpoint.check_point.next_checkpoint.coordinates_lon)**2)
if(dest<settings.MAX_CHECK_RADIUS):
cur_checkpoint.check_point = cur_checkpoint.check_point.next_checkpoint
cur_checkpoint.save()
if(cur_checkpoint.check_point.last):
team.active = False
team.finished = True
team.end = timezone.now()
team.save()
for gamer in team.gamers.all():
temp_prof = gamer.profile
temp_prof.points += game.points
temp_prof.save()
return Response({'ok':True}, status = status.HTTP_200_OK)
return Response({'ok':False, 'error':'You are too far from the place'}, status = status.HTTP_400_BAD_REQUEST)
return Response({'ok':False, 'error':temp_check_point.errors if temp_check_point.errors else 'Game finished' }, status = status.HTTP_400_BAD_REQUEST)
return Response({'ok':False, 'error':'You do not take part in game'}, status = status.HTTP_400_BAD_REQUEST)
return Response({'ok':False, 'error':'Game does not exist'}, status = status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def GameTakePartAPIView(request, slug):
game = Game.objects.filter(slug = slug)
if(game.exists()):
game = game.first()
profile = Profile.objects.get(user = request.user)
teams = Team.objects.filter(game = game)
team = []
for team_i in teams:
if(Gamer.objects.filter(profile = profile, team = team_i).exists()):
team = team_i
break
if(not team):
team = Team.objects.create(game = game, captain = profile)
if(game.co_op):
invite_token = sha1(bytes(f'{team.id}{game.id}{profile.id}{profile.user.username}','utf-8')).hexdigest()[:200]
InvitationToken.objects.create(team = team, token = invite_token)
gamer = Gamer.objects.create(team = team, profile = profile)
return Response({'ok':True}, status = status.HTTP_200_OK)
return Response({'ok':False, 'error':'You alredy take part in this game'}, status = status.HTTP_400_BAD_REQUEST)
return Response({'ok':False, 'error':'Game does not exist'}, status = status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def JoinTeamAPIView(request, slug, token):
game = Game.objects.filter(slug = slug)
if(game.exists()):
invite = InvitationToken.objects.filter(token = token)
if(invite.exists()):
profile = Profile.objects.get(user = request.user)
invite = invite.first()
team = invite.team
serializer = TeamSerializer(team)
gamers = serializer.data['gamers']
if(len(gamers) < team.game.max_gamers):
Gamer.objects.create(team = team, profile = profile)
return Response({'ok':True},status = status.HTTP_200_OK)
return Response({'ok':False, 'error':'Game finished or team is full'}, status = status.HTTP_400_BAD_REQUEST)
return Response({'ok':False, 'error':'Invalid invite link'}, status = status.HTTP_400_BAD_REQUEST)
return Response({'ok':False, 'error':'Game does not exist'}, status = status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def StartGameAPIView(request,slug):
game = Game.objects.filter(slug = slug)
if(game.exists()):
game = game.first()
profile = Profile.objects.get(user = request.user)
teams = Team.objects.filter(game = game, captain = profile)
team = []
for team_i in teams:
if(Gamer.objects.filter(profile = profile, team = team_i).exists()):
team = team_i
break
if(team):
if(team.captain == profile and not team.finished and not team.active):
team.active = True
team.start = timezone.now()
team.save()
first_check_point = CheckPoint.objects.get(game = team.game, start = True)
cur_checkpoint = CurrentCheckPoint.objects.create(team = team, check_point = first_check_point)
return Response({'ok':True}, status = status.HTTP_200_OK)
return Response({'ok':False, 'error':'You are not a captain or game finished or game in progress'}, status = status.HTTP_400_BAD_REQUEST)
return Response({'ok':False, 'error':'You do not take part in game'}, status = status.HTTP_400_BAD_REQUEST)
return Response({'ok':False, 'error':'Game does not exist'}, status = status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@permission_classes([~permissions.IsAuthenticated])
def UserRegisterAPIView(request):
serializer = UserRegisterSerializer(data = request.data)
if(serializer.is_valid()):
data = serializer.validated_data
if(User.objects.filter(username = data['username']).exists() or User.objects.filter(email = data['email']).exists()):
return Response({'ok':False, 'error':'User already exists'}, status = status.HTTP_400_BAD_REQUEST)
try:
validate_password(data['password'])
except Exception as errors:
return Response({'ok':False, 'error':errors}, status = status.HTTP_400_BAD_REQUEST)
user = User.objects.create(username = data['username'], email = data['email'], is_active = False)
user.set_password(data['password'])
user.save()
domain = get_current_site(request).domain
mail_subject = 'Account activation'
message = render_to_string('email_activtion.html',{
'user':user,
'domain': domain,
'uidb64': urlsafe_base64_encode(force_bytes(user.pk)),
'token': email_token_gen.make_token(user),
})
message_task = threading.Thread(target = async_email_send, args=( mail_subject, message, [data['email']] ))
message_task.start()
return Response({'ok':True}, status = status.HTTP_200_OK)
return Response({'ok':False, 'error':serializer.errors}, status = status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@permission_classes([permissions.AllowAny])
def UserActivationAPIView(request, uidb64, token):
try:
uid = force_text(urlsafe_base64_decode(uidb64))
except:
return Response({'ok':False, 'error':'Invalid activation link'}, status = status.HTTP_400_BAD_REQUEST)
user = User.objects.filter(id = uid)
if(user.exists()):
user = user.first()
if(email_token_gen.check_token(user, token)):
user.is_active = True
user.save()
return Response({'ok':True}, status = status.HTTP_200_OK)
return Response({'ok':False, 'error':'Invalid activation link'}, status = status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@permission_classes([~permissions.IsAuthenticated])
def UserResetPasswordRequestAPIView(request):
serializer = UserResetPasswordSerializer(data = request.data)
if(serializer.is_valid()):
data = serializer.validated_data
user = User.objects.filter(email = data['email'])
if(user.exists()):
user = user.first()
domain = get_current_site(request).domain
mail_subject = 'Password reset'
message = render_to_string('password_reset.html',{
'user':user,
'domain': domain,
'uidb64': urlsafe_base64_encode(force_bytes(user.pk)),
'token': password_token_gen.make_token(user),
})
message_task = threading.Thread(target = async_email_send, args=( mail_subject, message, [data['email']] ))
message_task.start()
return Response({'ok':True}, status = status.HTTP_200_OK)
return Response({'ok':False, 'error':'User does not exist'}, status = status.HTTP_400_BAD_REQUEST)
return Response({'ok':False, 'error':serializer.errors}, status = status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@permission_classes([~permissions.IsAuthenticated])
def UserResetPasswordAPIView(request, uidb64, token):
serializer = UserPasswordSerializer(data = request.data)
if(serializer.is_valid()):
data = serializer.validated_data
try:
validate_password(data['password'])
except Exception as errors:
return Response({'ok':False, 'error':errors}, status = status.HTTP_400_BAD_REQUEST)
uid = force_text(urlsafe_base64_decode(uidb64))
user = User.objects.filter(id = uid)
if(user.exists()):
user = user.first()
if(password_token_gen.check_token(user, token)):
user.set_password(data['password'])
user.save()
return Response({'ok':True}, status = status.HTTP_200_OK)
return Response({'ok':False, 'error':'Invalid password reset link'}, status = status.HTTP_400_BAD_REQUEST)
return Response({'ok':False, 'error':'User does not exist'}, status = status.HTTP_400_BAD_REQUEST)
return Response({'ok':False, 'error':serializer.errors}, status = status.HTTP_400_BAD_REQUEST)
@api_view(['GET'])
@permission_classes([permissions.IsAuthenticated])
def ProfileInfoAPIView(request):
profile = Profile.objects.get(user = request.user)
serializer = ProfileInfoSerializer(profile)
return Response({'ok':True, **serializer.data}, status = status.HTTP_200_OK)
@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def UserLogoutAPIView(request):
Token.objects.get(user = request).delete()
return Response(status = status.HTTP_200_OK)
def async_email_send(mail_subject, message, to_email):
mail_to_send = EmailMultiAlternatives(mail_subject, strip_tags(message), to=to_email)
mail_to_send.attach_alternative(message, 'text/html')
mail_to_send.send()