Commit 15439eb3 authored by Marius Engen's avatar Marius Engen
Browse files

Merge branch 'review/backend-workouts' into 'master'

Review/backend workouts

See merge request !7
parents 756a581e 193ec55a
Pipeline #172978 passed with stages
in 2 minutes and 4 seconds
from django.shortcuts import render """Comments views."""
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 comments.models import Comment, Like
from rest_framework import permissions
from comments.permissions import IsCommentVisibleToUser from comments.permissions import IsCommentVisibleToUser
from workouts.permissions import IsOwner, IsReadOnly
from comments.serializers import CommentSerializer, LikeSerializer from comments.serializers import CommentSerializer, LikeSerializer
from django.db.models import Q from workouts.permissions import IsOwner, IsReadOnly
from rest_framework.filters import OrderingFilter
# Create your views here.
class CommentList( class CommentList(
mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView
): ):
# queryset = Comment.objects.all() """List all comments, or create a new comment."""
serializer_class = CommentSerializer serializer_class = CommentSerializer
permission_classes = [permissions.IsAuthenticated] permission_classes = [permissions.IsAuthenticated]
filter_backends = [OrderingFilter] filter_backends = [OrderingFilter]
ordering_fields = ["timestamp"] ordering_fields = ["timestamp"]
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""Get a list of all comments."""
return self.list(request, *args, **kwargs) return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
"""Create a new comment."""
return self.create(request, *args, **kwargs) return self.create(request, *args, **kwargs)
def perform_create(self, serializer): def perform_create(self, serializer):
"""Create a new comment."""
serializer.save(owner=self.request.user) serializer.save(owner=self.request.user)
def get_queryset(self): def get_queryset(self):
"""Get the queryset for the list of comments."""
workout_pk = self.kwargs.get("pk") workout_pk = self.kwargs.get("pk")
qs = Comment.objects.none() queryset = Comment.objects.none()
if workout_pk: if workout_pk:
qs = Comment.objects.filter(workout=workout_pk) queryset = Comment.objects.filter(workout=workout_pk)
elif self.request.user: elif self.request.user:
"""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
- The comment is on a workout owned by the user
"""
# The code below is kind of duplicate of the one in ./permissions.py # The code below is kind of duplicate of the one in ./permissions.py
# We should replace it with a better solution. # We should replace it with a better solution.
# Or maybe not. # Or maybe not.
qs = Comment.objects.filter( queryset = Comment.objects.filter(
Q(workout__visibility="PU") Q(workout__visibility="PU")
| Q(owner=self.request.user) | Q(owner=self.request.user)
| ( | (
...@@ -54,15 +52,16 @@ class CommentList( ...@@ -54,15 +52,16 @@ class CommentList(
| Q(workout__owner=self.request.user) | Q(workout__owner=self.request.user)
).distinct() ).distinct()
return qs return queryset
# Details of comment
class CommentDetail( class CommentDetail(
mixins.RetrieveModelMixin, mixins.RetrieveModelMixin,
mixins.UpdateModelMixin, mixins.UpdateModelMixin,
mixins.DestroyModelMixin, mixins.DestroyModelMixin,
generics.GenericAPIView, generics.GenericAPIView,
): ):
"""Retrieve, update or delete a comment instance."""
queryset = Comment.objects.all() queryset = Comment.objects.all()
serializer_class = CommentSerializer serializer_class = CommentSerializer
permission_classes = [ permission_classes = [
...@@ -70,50 +69,60 @@ class CommentDetail( ...@@ -70,50 +69,60 @@ class CommentDetail(
] ]
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""Get a comment instance."""
return self.retrieve(request, *args, **kwargs) return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs): def put(self, request, *args, **kwargs):
"""Update a comment instance."""
return self.update(request, *args, **kwargs) return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
"""Delete a comment instance."""
return self.destroy(request, *args, **kwargs) return self.destroy(request, *args, **kwargs)
# List of likes
class LikeList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView): class LikeList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
"""List all likes, or create a new like."""
serializer_class = LikeSerializer serializer_class = LikeSerializer
permission_classes = [permissions.IsAuthenticated] permission_classes = [permissions.IsAuthenticated]
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""Get a list of all likes."""
return self.list(request, *args, **kwargs) return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
"""Create a new like."""
return self.create(request, *args, **kwargs) return self.create(request, *args, **kwargs)
def perform_create(self, serializer): def perform_create(self, serializer):
"""Create a new like."""
serializer.save(owner=self.request.user) serializer.save(owner=self.request.user)
def get_queryset(self): def get_queryset(self):
"""Get the queryset for the list of likes."""
return Like.objects.filter(owner=self.request.user) return Like.objects.filter(owner=self.request.user)
# Details of like
class LikeDetail( class LikeDetail(
mixins.RetrieveModelMixin, mixins.RetrieveModelMixin,
mixins.UpdateModelMixin, mixins.UpdateModelMixin,
mixins.DestroyModelMixin, mixins.DestroyModelMixin,
generics.GenericAPIView, generics.GenericAPIView,
): ):
"""Retrieve, update or delete a like instance."""
queryset = Like.objects.all() queryset = Like.objects.all()
serializer_class = LikeSerializer serializer_class = LikeSerializer
permission_classes = [permissions.IsAuthenticated] permission_classes = [permissions.IsAuthenticated]
_Detail = [] _Detail = []
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""Get a like instance."""
return self.retrieve(request, *args, **kwargs) return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs): def put(self, request, *args, **kwargs):
"""Update a like instance."""
return self.update(request, *args, **kwargs) return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
"""Delete a like instance."""
return self.destroy(request, *args, **kwargs) return self.destroy(request, *args, **kwargs)
[flake8]
ignore = E203, E266, E501, W503
max-line-length = 88
max-complexity = 18
select = B,C,E,F,W,T4
[pylint]
max-line-length = 88
[pylint.messages_control]
disable = C0330, C0326, C0301
# [isort]
# src_paths = ["backend"]
# multi_line_output=3
# include_trailing_comma=True
# force_grid_wrap=0
# use_parentheses=True
# line_length=88
# [tool:pytest]
# testpaths=tests
# DJANGO_SETTINGS_MODULE = config.settings.dev
# python_files = tests.py test_*.py *_tests.py
\ No newline at end of file
"""Admin model for User"""
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import Offer, AthleteFile
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from .forms import CustomUserChangeForm, CustomUserCreationForm from django.contrib.auth.admin import UserAdmin
# Register your models here. from .forms import CustomUserChangeForm, CustomUserCreationForm
from .models import AthleteFile, Offer
class CustomUserAdmin(UserAdmin): class CustomUserAdmin(UserAdmin):
"""Custom user admin model."""
add_form = CustomUserCreationForm add_form = CustomUserCreationForm
form = CustomUserChangeForm form = CustomUserChangeForm
model = get_user_model() model = get_user_model()
# list_display = UserAdmin.list_display + ('coach',)
fieldsets = UserAdmin.fieldsets + ((None, {"fields": ("coach",)}),) fieldsets = UserAdmin.fieldsets + ((None, {"fields": ("coach",)}),)
add_fieldsets = UserAdmin.add_fieldsets + ((None, {"fields": ("coach",)}),) add_fieldsets = UserAdmin.add_fieldsets + ((None, {"fields": ("coach",)}),)
......
from django.urls import path, include """URL for User model."""
from django.urls import path
from users import views from users import views
from rest_framework.urlpatterns import format_suffix_patterns
urlpatterns = [ urlpatterns = [
path("api/users/", views.UserList.as_view(), name="user-list"), path("api/users/", views.UserList.as_view(), name="user-list"),
......
import django """Views for the users app."""
from rest_framework import mixins, generics from django.contrib.auth import get_user_model
from workouts.mixins import CreateListModelMixin from django.db.models import Q
from rest_framework import permissions from rest_framework import generics, mixins, permissions
from rest_framework.parsers import FormParser, MultiPartParser
from rest_framework.permissions import (
IsAuthenticatedOrReadOnly,
)
from users.models import AthleteFile, Offer
from users.permissions import IsAthlete, IsCoach, IsCurrentUser
from users.serializers import ( from users.serializers import (
UserSerializer,
OfferSerializer,
AthleteFileSerializer, AthleteFileSerializer,
UserPutSerializer, OfferSerializer,
UserGetSerializer, UserGetSerializer,
UserPutSerializer,
UserSerializer,
) )
from rest_framework.permissions import ( from workouts.mixins import CreateListModelMixin
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.permissions import IsOwner, IsReadOnly from workouts.permissions import IsOwner, IsReadOnly
# Create your views here.
class UserList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView): class UserList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
"""List all users, or create a new user."""
serializer_class = UserSerializer serializer_class = UserSerializer
users = [] users = []
admins = [] admins = []
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""Get a list of all users.
If the user is an admin, return all users.
If the user is a coach, return all users that are athletes.
If the user is an athlete, return all users that are athletes.
"""
self.serializer_class = UserGetSerializer self.serializer_class = UserGetSerializer
return self.list(request, *args, **kwargs) return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
"""Create a new user."""
return self.create(request, *args, **kwargs) return self.create(request, *args, **kwargs)
def get_queryset(self): def get_queryset(self):
qs = get_user_model().objects.all() """Get the queryset for the list of users."""
queryset = get_user_model().objects.all()
if self.request.user: if self.request.user:
# Return the currently logged in user # Return the currently logged in user
status = self.request.query_params.get("user", None) status = self.request.query_params.get("user", None)
if status and status == "current": if status and status == "current":
qs = get_user_model().objects.filter(pk=self.request.user.pk) queryset = get_user_model().objects.filter(pk=self.request.user.pk)
return qs return queryset
class UserDetail( class UserDetail(
...@@ -54,12 +63,16 @@ class UserDetail( ...@@ -54,12 +63,16 @@ class UserDetail(
mixins.DestroyModelMixin, mixins.DestroyModelMixin,
generics.GenericAPIView, generics.GenericAPIView,
): ):
"""Retrieve, update or delete a user instance."""
lookup_field_options = ["pk", "username"] lookup_field_options = ["pk", "username"]
serializer_class = UserSerializer serializer_class = UserSerializer
queryset = get_user_model().objects.all() queryset = get_user_model().objects.all()
permission_classes = [permissions.IsAuthenticated & (IsCurrentUser | IsReadOnly)] permission_classes = [permissions.IsAuthenticated & (IsCurrentUser | IsReadOnly)]
def get_object(self): def get_object(self):
"""Get the object for the detail view."""
for field in self.lookup_field_options: for field in self.lookup_field_options:
if field in self.kwargs: if field in self.kwargs:
self.lookup_field = field self.lookup_field = field
...@@ -68,63 +81,76 @@ class UserDetail( ...@@ -68,63 +81,76 @@ class UserDetail(
return super().get_object() return super().get_object()
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""Get a user instance."""
self.serializer_class = UserGetSerializer self.serializer_class = UserGetSerializer
return self.retrieve(request, *args, **kwargs) return self.retrieve(request, *args, **kwargs)
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
"""Delete a user instance."""
return self.destroy(request, *args, **kwargs) return self.destroy(request, *args, **kwargs)
def put(self, request, *args, **kwargs): def put(self, request, *args, **kwargs):
"""Update a user instance."""
self.serializer_class = UserPutSerializer self.serializer_class = UserPutSerializer
return self.update(request, *args, **kwargs) return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs): def patch(self, request, *args, **kwargs):
"""Update a user instance."""
return self.partial_update(request, *args, **kwargs) return self.partial_update(request, *args, **kwargs)
class OfferList( class OfferList(
mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView
): ):
"""List all offers, or create a new offer."""
permission_classes = [IsAuthenticatedOrReadOnly] permission_classes = [IsAuthenticatedOrReadOnly]
serializer_class = OfferSerializer serializer_class = OfferSerializer
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""Get a list of all offers."""
return self.list(request, *args, **kwargs) return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
"""Create a new offer."""
return self.create(request, *args, **kwargs) return self.create(request, *args, **kwargs)
def perform_create(self, serializer): def perform_create(self, serializer):
"""Set the owner of the offer."""
serializer.save(owner=self.request.user) serializer.save(owner=self.request.user)
def get_queryset(self): def get_queryset(self):
qs = Offer.objects.none() """Get the queryset for the list of offers."""
result = Offer.objects.none() result = Offer.objects.none()
if self.request.user: if self.request.user:
qs = Offer.objects.filter( queryset = Offer.objects.filter(
Q(owner=self.request.user) | Q(recipient=self.request.user) Q(owner=self.request.user) | Q(recipient=self.request.user)
).distinct() ).distinct()
qp = self.request.query_params query_parameters = self.request.query_params
u = self.request.user user = self.request.user
# filtering by status (if provided) # filtering by status (if provided)
s = qp.get("status", None) status = query_parameters.get("status", None)
if s is not None and self.request is not None: if status is not None and self.request is not None:
qs = qs.filter(status=s) queryset = queryset.filter(status=status)
if qp.get("status", None) is None: if query_parameters.get("status", None) is None:
qs = Offer.objects.filter(Q(owner=u)).distinct() queryset = Offer.objects.filter(Q(owner=user)).distinct()
# filtering by category (sent or received) # filtering by category (sent or received)
c = qp.get("category", None) category = query_parameters.get("category", None)
if c is not None and qp is not None: if category is not None and query_parameters is not None:
if c == "sent": if category == "sent":
qs = qs.filter(owner=u) queryset = queryset.filter(owner=user)
elif c == "received": elif category == "received":
qs = qs.filter(recipient=u) queryset = queryset.filter(recipient=user)
return qs return queryset
else:
return result return result
class OfferDetail( class OfferDetail(
...@@ -133,20 +159,26 @@ class OfferDetail( ...@@ -133,20 +159,26 @@ class OfferDetail(
mixins.DestroyModelMixin, mixins.DestroyModelMixin,
generics.GenericAPIView, generics.GenericAPIView,
): ):
"""Retrieve, update or delete a offer instance."""
permission_classes = [IsAuthenticatedOrReadOnly] permission_classes = [IsAuthenticatedOrReadOnly]
queryset = Offer.objects.all() queryset = Offer.objects.all()
serializer_class = OfferSerializer serializer_class = OfferSerializer
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""Get a offer instance."""
return self.retrieve(request, *args, **kwargs) return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs): def put(self, request, *args, **kwargs):
"""Update a offer instance."""
return self.update(request, *args, **kwargs) return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs): def patch(self, request, *args, **kwargs):
"""Update a offer instance."""
return self.partial_update(request, *args, **kwargs) return self.partial_update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
"""Delete a offer instance."""
return self.destroy(request, *args, **kwargs) return self.destroy(request, *args, **kwargs)