mirror of
https://github.com/workhardbekind/workout-challenge.git
synced 2026-07-04 09:23:32 -04:00
first commit
This commit is contained in:
commit
e7f627801f
152 changed files with 35352 additions and 0 deletions
181
src-backend/custom_user/views.py
Normal file
181
src-backend/custom_user/views.py
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
import time, datetime
|
||||
import requests
|
||||
from rest_framework import viewsets
|
||||
from rest_framework.permissions import BasePermission, IsAdminUser, SAFE_METHODS, AllowAny
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from django.db.models import Q
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from celery.exceptions import TimeoutError
|
||||
from django.utils import timezone
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
|
||||
from .serializers import PasswordResetSerializer, PasswordResetConfirmSerializer
|
||||
from .models import CustomUser
|
||||
from .serializers import CustomUserSerializer
|
||||
from .filters import CustomUserFilter
|
||||
from .strava import sync_strava
|
||||
|
||||
class IsOwnerOrReadOnly(BasePermission):
|
||||
""" Permission class to only allow admins and owner to edit or delete entry """
|
||||
def has_permission(self, request, view):
|
||||
# Only authenticated users
|
||||
if request.user.is_authenticated:
|
||||
return True
|
||||
return False
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
# Read requests always allowed
|
||||
if request.method in SAFE_METHODS:
|
||||
return True # allow GET, HEAD, OPTIONS (GET is filtered at viweset level to only show allowed entries)
|
||||
# Only workout user can edit workout
|
||||
if hasattr(obj, 'user') and obj.user == request.user:
|
||||
return True
|
||||
# Only owner of competition can modify
|
||||
elif hasattr(obj, 'owner') and obj.owner == request.user:
|
||||
return True
|
||||
# Only owner can modify goals and awards
|
||||
elif hasattr(obj, 'competition') and hasattr(obj.competition, 'owner') and obj.competition.owner == request.user:
|
||||
return True
|
||||
# If admin allow all requests
|
||||
if bool(request.user and request.user.is_staff):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class UserPermissionClass(BasePermission):
|
||||
""" Allow unauthenticated users to POST data - i.e. for registration """
|
||||
def has_permission(self, request, view):
|
||||
# Only create new requsts - i.e. POST
|
||||
if request.method in ('POST', 'OPTIONS'):
|
||||
return True
|
||||
return request.user.is_authenticated
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
return obj.pk == request.user.pk
|
||||
|
||||
|
||||
class CustomUserViewSet(viewsets.ModelViewSet):
|
||||
#queryset = Competition.objects.all()
|
||||
serializer_class = CustomUserSerializer
|
||||
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_class = CustomUserFilter
|
||||
|
||||
permission_classes = [UserPermissionClass]
|
||||
|
||||
def get_queryset(self):
|
||||
# return all competitions the user is owner of or a participant of
|
||||
#time.sleep(3) # throttle for testing
|
||||
return CustomUser.objects.filter(Q(pk=self.request.user.pk) | Q(my_competitions__in=self.request.user.my_competitions.all())).distinct().order_by('username', 'id')
|
||||
|
||||
def get_object(self):
|
||||
lookup_value = self.kwargs.get(self.lookup_field)
|
||||
|
||||
# Modify filter if I ask for myself instead of the id number
|
||||
if str(lookup_value).lower() in ['me', 'my', 'myself', 'i']:
|
||||
lookup_value = self.request.user.id
|
||||
|
||||
return get_object_or_404(self.get_queryset(), pk=lookup_value)
|
||||
|
||||
|
||||
class PasswordResetView(APIView):
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
def post(self, request):
|
||||
serializer = PasswordResetSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
serializer.save(request=request)
|
||||
return Response({"detail": "Password reset e-mail sent."})
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class PasswordResetConfirmView(APIView):
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
def post(self, request):
|
||||
serializer = PasswordResetConfirmSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return Response({"detail": "Password has been reset."})
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class LinkStravaView(APIView):
|
||||
""" API post view for users to link with Strava. """
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request, code):
|
||||
user = request.user
|
||||
client_id = settings.STRAVA_CLIENT_ID
|
||||
client_secret = settings.STRAVA_CLIENT_SECRET
|
||||
|
||||
if client_id == 1234321 or client_secret == "ReplaceWithClientSecret":
|
||||
return Response({"message": "Sever configuration error - STRAVA_CLIENT_ID and/or STRAVA_CLIENT_SECRET are not set."}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
|
||||
|
||||
response = requests.post(
|
||||
url='https://www.strava.com/oauth/token',
|
||||
data={
|
||||
'client_id': client_id,
|
||||
'client_secret': client_secret,
|
||||
'code': code,
|
||||
'grant_type': 'authorization_code'
|
||||
}
|
||||
)
|
||||
|
||||
if response.ok is False:
|
||||
return Response({"message": "Invalid Strava linkage code"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
strava_tokens = response.json()
|
||||
setattr(user, 'strava_refresh_token', strava_tokens.get('refresh_token', None))
|
||||
setattr(user, 'strava_athlete_id', strava_tokens.get('athlete', {}).get('id', None))
|
||||
user.save()
|
||||
|
||||
cache.set(f"strava_access_token_{user.id}", strava_tokens.get('access_token', None), int(strava_tokens.get('expires_in', 21600)) - 60)
|
||||
try:
|
||||
running_task = sync_strava.delay(user__id=user.id, start_datetime=datetime.datetime.now() - datetime.timedelta(days=43))
|
||||
try:
|
||||
running_task.get(timeout=100)
|
||||
except TimeoutError:
|
||||
print(f"Strava sync task is still running ({running_task.id}). Don't let the user wait so long.")
|
||||
except requests.exceptions.HTTPError as err:
|
||||
if '401 Client Error: Unauthorized' in str(err):
|
||||
return Response({'message': 'Access to activities denied by Strava. Not sufficient permissions to download activities.'}, status=status.HTTP_403_FORBIDDEN)
|
||||
else:
|
||||
raise Response(err.response.json(), status=err.response.status_code)
|
||||
|
||||
return Response({"message": "Successfully linked Strava."}, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class UnlinkStravaView(APIView):
|
||||
""" API post view for users to unlink Strava. """
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
user = request.user
|
||||
setattr(user, 'strava_refresh_token', None)
|
||||
setattr(user, 'strava_athlete_id', None)
|
||||
user.save()
|
||||
|
||||
return Response({"message": "Successfully unlinked Strava."}, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class SyncStravaView(APIView):
|
||||
""" API get view for users to sync Strava. """
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get(self, request):
|
||||
user = request.user
|
||||
|
||||
if user.strava_refresh_token is None or user.strava_refresh_token == '':
|
||||
return Response({"message": "Strava is not linked."}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if user.strava_last_synced_at is None or user.strava_last_synced_at == '' or user.strava_last_synced_at < (timezone.now() - datetime.timedelta(minutes=59)):
|
||||
sync_strava(user__id=user.id)
|
||||
return Response({"message": f"Successfully synced Strava."}, status=status.HTTP_200_OK)
|
||||
|
||||
return Response({"message": "Too many requests! You can only request a Strava sync every 60 minutes."}, status=status.HTTP_429_TOO_MANY_REQUESTS)
|
||||
Loading…
Add table
Add a link
Reference in a new issue