diff --git a/backend/secfit/comments/views.py b/backend/secfit/comments/views.py index b74d0f208c9bcf06ee49817541d47742767f0b7d..bb37fb1b432be7fb4908b103f6a34ef8c14f26c7 100644 --- a/backend/secfit/comments/views.py +++ b/backend/secfit/comments/views.py @@ -1,18 +1,15 @@ -from django.shortcuts import render -from rest_framework import generics, mixins +from django.db.models import Q +from rest_framework import generics, mixins, permissions +from rest_framework.filters import OrderingFilter from comments.models import Comment, Like -from rest_framework import permissions from comments.permissions import IsCommentVisibleToUser -from workouts.permissions import IsOwner, IsReadOnly from comments.serializers import CommentSerializer, LikeSerializer -from django.db.models import Q -from rest_framework.filters import OrderingFilter +from workouts.permissions import IsOwner, IsReadOnly # Create your views here. class CommentList( mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView ): - # queryset = Comment.objects.all() serializer_class = CommentSerializer permission_classes = [permissions.IsAuthenticated] filter_backends = [OrderingFilter] @@ -29,12 +26,13 @@ class CommentList( def get_queryset(self): workout_pk = self.kwargs.get("pk") - qs = Comment.objects.none() + query_set = Comment.objects.none() if workout_pk: - qs = Comment.objects.filter(workout=workout_pk) + query_set = Comment.objects.filter(workout=workout_pk) elif self.request.user: - """A comment should be visible to the requesting user if any of the following hold: + """ + A comment should be visible to the requesting user if any of the following hold: - The comment is on a public visibility workout - The comment was written by the user - The comment is on a coach visibility workout and the user is the workout owner's coach @@ -43,8 +41,7 @@ class CommentList( # The code below is kind of duplicate of the one in ./permissions.py # We should replace it with a better solution. # Or maybe not. - - qs = Comment.objects.filter( + query_set = Comment.objects.filter( Q(workout__visibility="PU") | Q(owner=self.request.user) | ( @@ -54,7 +51,7 @@ class CommentList( | Q(workout__owner=self.request.user) ).distinct() - return qs + return query_set # Details of comment class CommentDetail( diff --git a/backend/secfit/users/admin.py b/backend/secfit/users/admin.py index fc0af23c4473e29bcc06045aebfdd0d21989d22d..9c410c881a8cf639b4ebf7edaf114d03a8c39780 100644 --- a/backend/secfit/users/admin.py +++ b/backend/secfit/users/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin +from django.contrib.auth import get_user_model from django.contrib.auth.admin import UserAdmin from .models import Offer, AthleteFile -from django.contrib.auth import get_user_model from .forms import CustomUserChangeForm, CustomUserCreationForm # Register your models here. @@ -11,7 +11,6 @@ class CustomUserAdmin(UserAdmin): add_form = CustomUserCreationForm form = CustomUserChangeForm model = get_user_model() - # list_display = UserAdmin.list_display + ('coach',) fieldsets = UserAdmin.fieldsets + ((None, {"fields": ("coach",)}),) add_fieldsets = UserAdmin.add_fieldsets + ((None, {"fields": ("coach",)}),) diff --git a/backend/secfit/users/serializers.py b/backend/secfit/users/serializers.py index e46a4c5084f2b380ba32728c6a458eeafcf6af0f..d159be0e1b887ad74e854e4741d71ec7eb9aff34 100644 --- a/backend/secfit/users/serializers.py +++ b/backend/secfit/users/serializers.py @@ -1,7 +1,7 @@ from rest_framework import serializers +from django import forms from django.contrib.auth import get_user_model, password_validation from users.models import Offer, AthleteFile -from django import forms class UserSerializer(serializers.HyperlinkedModelSerializer): @@ -36,6 +36,9 @@ class UserSerializer(serializers.HyperlinkedModelSerializer): password = data.get("password") password1 = data.get("password1") + if password != password1: + raise serializers.ValidationError("The passwords must match") + try: password_validation.validate_password(password) except forms.ValidationError as error: @@ -53,7 +56,16 @@ class UserSerializer(serializers.HyperlinkedModelSerializer): street_address = validated_data["street_address"] favourite_exercise = validated_data["favourite_exercise"] main_gym = validated_data["main_gym"] - user_obj = get_user_model()(username=username, email=email, phone_number=phone_number, country=country, city=city, street_address=street_address, favourite_exercise=favourite_exercise, main_gym=main_gym) + user_obj = get_user_model()( + username=username, + email=email, + phone_number=phone_number, + country=country, + city=city, + street_address=street_address, + favourite_exercise=favourite_exercise, + main_gym=main_gym + ) user_obj.set_password(password) user_obj.save() diff --git a/backend/secfit/users/tests/test_serializers.py b/backend/secfit/users/tests/test_serializers.py index 0f5b2d17fc2319a048bdcca78570800e0339460f..327f1b3e64c9f58fe96e6c87ed2d365ae58a43e6 100644 --- a/backend/secfit/users/tests/test_serializers.py +++ b/backend/secfit/users/tests/test_serializers.py @@ -1,20 +1,23 @@ -from threading import local -from django.test import TestCase, RequestFactory, Client -from users.models import User +from django.test import TestCase from users.serializers import UserSerializer +from rest_framework import serializers # Create your tests here. class UserSerializerTestCase(TestCase): - # def setUp(self): - #anything? def test_validate_password(self): - print("User tests") my_pass = "pw" local_seri = UserSerializer(data={"password": my_pass, "password1": my_pass}) self.assertEqual(local_seri.validate_password(my_pass), my_pass) + def test_invalid_password(self): + my_pass = "pw" + invalid_pass = "hehe" + + local_seri = UserSerializer(data={"password": my_pass, "password1": invalid_pass}) + self.assertRaises(serializers.ValidationError, local_seri.validate_password, my_pass) + def test_create(self): username = "jorsi" email = "some@mail.com" diff --git a/backend/secfit/users/urls.py b/backend/secfit/users/urls.py index 282ffd7c338b96a1161f7e361a2c981fce6780f2..00dfc4c35a5647a98e78f6b1e78eaf7b7948d67e 100644 --- a/backend/secfit/users/urls.py +++ b/backend/secfit/users/urls.py @@ -1,6 +1,5 @@ -from django.urls import path, include +from django.urls import path from users import views -from rest_framework.urlpatterns import format_suffix_patterns urlpatterns = [ path("api/users/", views.UserList.as_view(), name="user-list"), diff --git a/backend/secfit/users/views.py b/backend/secfit/users/views.py index 245b1c2277e3b250e20afb16b5a4b7436b46d388..761c889c6c3ae6d64d086a993b6e95862c7762f6 100644 --- a/backend/secfit/users/views.py +++ b/backend/secfit/users/views.py @@ -1,7 +1,8 @@ -import django -from rest_framework import mixins, generics -from workouts.mixins import CreateListModelMixin -from rest_framework import permissions +from rest_framework import mixins, generics, permissions +from rest_framework.permissions import ( + IsAuthenticatedOrReadOnly, +) +from rest_framework.parsers import MultiPartParser, FormParser from users.serializers import ( UserSerializer, OfferSerializer, @@ -10,19 +11,12 @@ from users.serializers import ( UserGetSerializer, UserProfilePostSerializer ) -from rest_framework.permissions import ( - AllowAny, - IsAdminUser, - IsAuthenticated, - IsAuthenticatedOrReadOnly, -) from users.models import Offer, AthleteFile -from django.contrib.auth import get_user_model -from django.db.models import Q -from django.shortcuts import get_object_or_404 -from rest_framework.parsers import MultiPartParser, FormParser from users.permissions import IsCurrentUser, IsAthlete, IsCoach +from workouts.mixins import CreateListModelMixin from workouts.permissions import IsOwner, IsReadOnly +from django.contrib.auth import get_user_model +from django.db.models import Q # Create your views here. class UserList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView): @@ -103,33 +97,31 @@ class OfferList( serializer.save(owner=self.request.user) def get_queryset(self): - qs = Offer.objects.none() result = Offer.objects.none() if self.request.user: - qs = Offer.objects.filter( + query_set = Offer.objects.filter( Q(owner=self.request.user) | Q(recipient=self.request.user) ).distinct() - qp = self.request.query_params - u = self.request.user + query_param = self.request.query_params + user = self.request.user # filtering by status (if provided) - s = qp.get("status", None) - if s is not None and self.request is not None: - qs = qs.filter(status=s) - if qp.get("status", None) is None: - qs = Offer.objects.filter(Q(owner=u)).distinct() + filter_status = query_param.get("status", None) + if filter_status is not None and self.request is not None: + query_set = query_set.filter(status=filter_status) + if query_param.get("status", None) is None: + query_set = Offer.objects.filter(Q(owner=user)).distinct() # filtering by category (sent or received) - c = qp.get("category", None) - if c is not None and qp is not None: - if c == "sent": - qs = qs.filter(owner=u) - elif c == "received": - qs = qs.filter(recipient=u) - return qs - else: - return result + filter_category = query_param.get("category", None) + if filter_category is not None and query_param is not None: + if filter_category == "sent": + query_set = query_set.filter(owner=user) + elif filter_category == "received": + query_set = query_set.filter(recipient=user) + return query_set + return result class OfferDetail( diff --git a/backend/secfit/workouts/models.py b/backend/secfit/workouts/models.py index 3148639aad3f65def0b2d043062f11ef8f9cb76f..0bd31bd4a853f17ca34da70d9f3c10a83b8f1fe6 100644 --- a/backend/secfit/workouts/models.py +++ b/backend/secfit/workouts/models.py @@ -65,7 +65,7 @@ class Workout(models.Model): ordering = ["-date"] def __str__(self): - return self.name + return str(self.name) class Exercise(models.Model): @@ -90,7 +90,7 @@ class Exercise(models.Model): unit = models.CharField(max_length=50) def __str__(self): - return self.name + return str(self.name) class Goal(models.Model): """Django model for an goal type that users can create. @@ -108,7 +108,7 @@ class Goal(models.Model): exercise = models.TextField() def __str__(self): - return self.name + return str(self.name) class ExerciseInstance(models.Model): @@ -174,4 +174,4 @@ class RememberMe(models.Model): remember_me = models.CharField(max_length=500) def __str__(self): - return self.remember_me + return str(self.remember_me) diff --git a/backend/secfit/workouts/serializers.py b/backend/secfit/workouts/serializers.py index 98036eec0661f5b05201221987788c30cf6b96eb..c533c14d1503141117307be92b2403d79f9cda9f 100644 --- a/backend/secfit/workouts/serializers.py +++ b/backend/secfit/workouts/serializers.py @@ -162,29 +162,32 @@ class WorkoutSerializer(serializers.HyperlinkedModelSerializer): for i in range(len(exercise_instances_data), len(exercise_instances.all())): exercise_instances.all()[i].delete() - # Handle WorkoutFiles if "files" in validated_data: - files_data = validated_data.pop("files") - files = instance.files + self.handle_workout_files(instance, validated_data) - for file, file_data in zip(files.all(), files_data): - file.file = file_data.get("file", file.file) + return instance + + # Handle WorkoutFiles + def handle_workout_files(self, instance, validated_data): + files_data = validated_data.pop("files") + files = instance.files + + for file, file_data in zip(files.all(), files_data): + file.file = file_data.get("file", file.file) # If new files have been added, creating new WorkoutFiles - if len(files_data) > len(files.all()): - for i in range(len(files.all()), len(files_data)): - WorkoutFile.objects.create( + if len(files_data) > len(files.all()): + for i in range(len(files.all()), len(files_data)): + WorkoutFile.objects.create( workout=instance, owner=instance.owner, file=files_data[i].get("file"), ) # Else if files have been removed, delete WorkoutFiles - elif len(files_data) < len(files.all()): - for i in range(len(files_data), len(files.all())): - files.all()[i].delete() - - return instance + elif len(files_data) < len(files.all()): + for i in range(len(files_data), len(files.all())): + files.all()[i].delete() def get_owner_username(self, obj): """Returns the owning user's username @@ -213,7 +216,10 @@ class ExerciseSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Exercise - fields = ["url", "id", "name", "description", "duration", "calories", "muscleGroup", "unit", "instances"] + fields = [ + "url", "id", "name", "description", "duration", + "calories", "muscleGroup", "unit", "instances" + ] class GoalSerializer(serializers.HyperlinkedModelSerializer): """Serializer for an Goal. Hyperlinks are used for relationships by default. diff --git a/backend/secfit/workouts/views.py b/backend/secfit/workouts/views.py index 9b64e09924f9f43b86271b758cc2829205dd3af9..5a3750901693e37782a69715b17f5ebae62c9289 100644 --- a/backend/secfit/workouts/views.py +++ b/backend/secfit/workouts/views.py @@ -1,16 +1,16 @@ """Contains views for the workouts application. These are mostly class-based views. """ -from rest_framework import generics, mixins -from rest_framework import permissions - +from collections import namedtuple +import base64 +import pickle +from rest_framework import generics, mixins, permissions, filters from rest_framework.parsers import ( JSONParser, ) from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework.reverse import reverse -from django.db.models import Q -from rest_framework import filters +from rest_framework_simplejwt.tokens import RefreshToken from workouts.parsers import MultipartJsonParser from workouts.permissions import ( IsOwner, @@ -26,12 +26,8 @@ from workouts.models import Workout, Exercise, ExerciseInstance, Goal, WorkoutFi from workouts.serializers import WorkoutSerializer, ExerciseSerializer, GoalSerializer from workouts.serializers import RememberMeSerializer from workouts.serializers import ExerciseInstanceSerializer, WorkoutFileSerializer +from django.db.models import Q from django.core.exceptions import PermissionDenied -from rest_framework_simplejwt.tokens import RefreshToken -from rest_framework.response import Response -import json -from collections import namedtuple -import base64, pickle from django.core.signing import Signer @@ -66,16 +62,15 @@ class RememberMe( serializer_class = RememberMeSerializer def get(self, request): - if request.user.is_authenticated == False: + if not request.user.is_authenticated: raise PermissionDenied - else: - return Response({"remember_me": self.rememberme()}) + return Response({"remember_me": self.rememberme()}) def post(self, request): - cookieObject = namedtuple("Cookies", request.COOKIES.keys())( + coockie_object = namedtuple("Cookies", request.COOKIES.keys())( *request.COOKIES.values() ) - user = self.get_user(cookieObject) + user = self.get_user(coockie_object) refresh = RefreshToken.for_user(user) return Response( { @@ -84,13 +79,14 @@ class RememberMe( } ) - def get_user(self, cookieObject): - decode = base64.b64decode(cookieObject.remember_me) + def get_user(self, coockie_object): + decode = base64.b64decode(coockie_object.remember_me) user, sign = pickle.loads(decode) # Validate signature if sign == self.sign_user(user): return user + return None def rememberme(self): creds = [self.request.user, self.sign_user(str(self.request.user))] @@ -132,18 +128,18 @@ class WorkoutList( serializer.save(owner=self.request.user) def get_queryset(self): - qs = Workout.objects.none() + query_set = Workout.objects.none() if self.request.user: # A workout should be visible to the requesting user if any of the following hold: # - The workout has public visibility # - The owner of the workout is the requesting user # - The workout has coach visibility and the requesting user is the owner's coach - qs = Workout.objects.filter( + query_set = Workout.objects.filter( Q(visibility="PU") | (Q(visibility="CO") & Q(owner__coach=self.request.user)) ).distinct() - return qs + return query_set class WorkoutDetail( @@ -288,9 +284,9 @@ class ExerciseInstanceList( return self.create(request, *args, **kwargs) def get_queryset(self): - qs = ExerciseInstance.objects.none() + query_set = ExerciseInstance.objects.none() if self.request.user: - qs = ExerciseInstance.objects.filter( + query_set = ExerciseInstance.objects.filter( Q(workout__owner=self.request.user) | ( (Q(workout__visibility="CO") | Q(workout__visibility="PU")) @@ -298,7 +294,7 @@ class ExerciseInstanceList( ) ).distinct() - return qs + return query_set class ExerciseInstanceDetail( @@ -351,9 +347,9 @@ class WorkoutFileList( serializer.save(owner=self.request.user) def get_queryset(self): - qs = WorkoutFile.objects.none() + query_set = WorkoutFile.objects.none() if self.request.user: - qs = WorkoutFile.objects.filter( + query_set = WorkoutFile.objects.filter( Q(owner=self.request.user) | Q(workout__owner=self.request.user) | ( @@ -362,7 +358,7 @@ class WorkoutFileList( ) ).distinct() - return qs + return query_set class WorkoutFileDetail(