Commit b58ad046 authored by Erlend Ydse's avatar Erlend Ydse
Browse files

Merge branch 'refactor-manual-review' into 'master'

Refactor the project by manual review

See merge request !7
parents 12b2055a b15f41fc
Pipeline #128681 failed with stages
in 13 seconds
......@@ -5,6 +5,12 @@ FROM python:3.8.5-slim
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
# Add sqlite3 to enable dbshell command for managing the database
RUN apt-get update -y && apt-get install sqlite3 -y
# Upgrade pip
RUN pip install --upgrade pip
# Make a new directory to put our code in.
RUN mkdir /code
......@@ -15,12 +21,6 @@ WORKDIR /code
# Copy the of the code.
COPY . /code/
# Add sqlite3 to enable dbshell command for managing the database
RUN apt-get update -y && apt-get install sqlite3 -y
# Upgrade pip
RUN pip install --upgrade pip
# Install the requirements.
RUN pip install -r requirements.txt
......
from django.contrib import admin
# Register your models here.
from .models import Comment
from comments.models import Comment
admin.site.register(Comment)
# Generated by Django 3.1 on 2021-03-12 10:35
# Generated by Django 3.1 on 2021-04-15 15:35
from django.db import migrations
......@@ -6,12 +6,11 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('friends', '0001_initial'),
('comments', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='friendrequest',
options={'ordering': ['-timestamp']},
migrations.DeleteModel(
name='Like',
),
]
from django.contrib.auth import get_user_model
from django.db import models
from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.urls import reverse
from django.db import models
from django.contrib.auth import get_user_model
from workouts.models import Workout
# Create your models here.
class Comment(models.Model):
"""Django model for a comment left on a workout.
......@@ -30,19 +24,3 @@ class Comment(models.Model):
class Meta:
ordering = ["-timestamp"]
class Like(models.Model):
"""Django model for a reaction to a comment.
Attributes:
owner: Who liked the comment
comment: The comment that was liked
timestamp: When the like occurred.
"""
owner = models.ForeignKey(
get_user_model(), on_delete=models.CASCADE, related_name="likes"
)
comment = models.ForeignKey(Comment, on_delete=models.CASCADE, related_name="likes")
timestamp = models.DateTimeField(auto_now_add=True)
from rest_framework import serializers
from rest_framework.serializers import HyperlinkedRelatedField
from comments.models import Comment, Like
from comments.models import Comment
from workouts.models import Workout
......@@ -13,14 +14,3 @@ class CommentSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Comment
fields = ["url", "id", "owner", "workout", "content", "timestamp"]
class LikeSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source="owner.username")
comment = HyperlinkedRelatedField(
queryset=Comment.objects.all(), view_name="comment-detail"
)
class Meta:
model = Like
fields = ["url", "id", "owner", "comment", "timestamp"]
from django.test import TestCase
# Create your tests here.
from django.urls import path, include
from comments.models import Comment, Like
from comments.views import CommentList, CommentDetail, LikeList, LikeDetail
from rest_framework.urlpatterns import format_suffix_patterns
from django.urls import path
from comments.views import CommentDetail, CommentList
urlpatterns = [
path("api/comments/", CommentList.as_view(), name="comment-list"),
path("api/comments/<int:pk>/", CommentDetail.as_view(), name="comment-detail"),
path("api/likes/", LikeList.as_view(), name="like-list"),
path("api/likes/<int:pk>/", LikeDetail.as_view(), name="like-detail"),
path("comments/", CommentList.as_view(), name="comment-list"),
path("comments/<int:pk>/", CommentDetail.as_view(), name="comment-detail"),
]
from django.shortcuts import render
from rest_framework import generics, mixins
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 import generics, mixins, permissions
from rest_framework.filters import OrderingFilter
from rest_framework.response import Response
from rest_framework.status import HTTP_400_BAD_REQUEST
from comments.models import Comment
from comments.permissions import IsCommentVisibleToUser
from comments.serializers import CommentSerializer
from workouts.models import Workout
from workouts.permissions import IsOwner, IsReadOnly
# Create your views here.
class CommentList(
mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView
):
......@@ -22,6 +24,17 @@ class CommentList(
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
workout_id = request.data["workout"].split("/")[-2]
workout = Workout.objects.get(pk=workout_id)
owns_workout = False
if workout:
owns_workout = workout.owner == request.user
is_public = workout.visibility == "PU"
can_access_as_coach = workout.owner.coach == request.user and (
workout.visibility == "PU" or workout.visibility == "CO"
)
if not (owns_workout or is_public or can_access_as_coach):
return Response(status=HTTP_400_BAD_REQUEST)
return self.create(request, *args, **kwargs)
def perform_create(self, serializer):
......@@ -56,7 +69,6 @@ class CommentList(
return qs
# Details of comment
class CommentDetail(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
......@@ -77,43 +89,3 @@ class CommentDetail(
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
# List of likes
class LikeList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
serializer_class = LikeSerializer
permission_classes = [permissions.IsAuthenticated]
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
def get_queryset(self):
return Like.objects.filter(owner=self.request.user)
# Details of like
class LikeDetail(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView,
):
queryset = Like.objects.all()
serializer_class = LikeSerializer
permission_classes = [permissions.IsAuthenticated]
_Detail = []
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
from django.contrib import admin
from friends.models import FriendList, FriendRequest
class FriendListAdmin(admin.ModelAdmin):
list_filter = ['user']
list_display = ['user']
search_fields = ['user']
readonly_fields = ['user']
class Meta:
model = FriendList
admin.site.register(FriendList, FriendListAdmin)
class FriendRequestAdmin(admin.ModelAdmin):
list_filter = ['sender', 'receiver']
list_display = ['sender', 'receiver']
search_fields = ['sender__username', 'receiver__username']
class Meta:
model = FriendRequest
admin.site.register(FriendRequest, FriendRequestAdmin)
from django.apps import AppConfig
class FriendsConfig(AppConfig):
name = 'friends'
# Generated by Django 3.1 on 2021-03-05 17:40
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='FriendRequest',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_active', models.BooleanField(blank=True, default=True)),
('timestamp', models.DateTimeField(auto_now_add=True)),
('receiver', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='receiver', to=settings.AUTH_USER_MODEL)),
('sender', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sender', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='FriendList',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('friends', models.ManyToManyField(blank=True, related_name='friends', to=settings.AUTH_USER_MODEL)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='user', to=settings.AUTH_USER_MODEL)),
],
),
]
from django.db import models
from django.contrib.auth import get_user_model
class FriendList(models.Model):
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, related_name="user")
friends = models.ManyToManyField(get_user_model(), blank=True, related_name="friends")
def __str__(self):
return self.user.username
def add(self, other_user):
"""
Add a new friend to the list of user's friends
:param other_user: The user that should be added as a friend
"""
if other_user not in self.friends.all():
self.friends.add(other_user)
self.save()
def remove(self, other_user):
"""
Remove a friend from the list of user's friends
:param other_user: The user which should be removed as a friend
"""
if other_user in self.friends.all():
self.friends.remove(other_user)
self.save()
def is_friend(self, other_user):
"""
Check if user is a mutual friend of self
:param other_user: User to check friendship with
:return: True if friends, False if not friends
"""
return other_user in self.friends.all()
def unfriend(self, other_user):
"""
Unfriend a friend of the user
:param self: The user terminating the friendship
:param other_user: The friend who is being removed
:return:
"""
self.remove(other_user)
removee_friend_list = FriendList.objects.get(user=other_user)
removee_friend_list.remove(self.user)
class FriendRequest(models.Model):
sender = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name="sender")
receiver = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name="receiver")
is_active = models.BooleanField(blank=True, null=False, default=True)
timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ["-timestamp"]
def __str__(self):
return self.sender.username + " " + self.sender.username
def accept(self):
"""
The request is accepted. Updates both the friend lists of both the sender and receiver fields.
"""
receiver_friend_list = FriendList.objects.get(user=self.receiver)
sender_friend_list = FriendList.objects.get(user=self.sender)
try:
try:
sender_friend_list.add(self.receiver)
except Exception:
raise Exception("Could not add receiver to sender's friend list")
try:
receiver_friend_list.add(self.sender)
except Exception:
raise Exception("Could not add sender to receiver's friend list")
finally:
self.is_active = False
self.save()
def decline(self):
"""
The request is declined and the 'is_active' field is set to false.
"""
self.is_active = False
self.save()
def cancel(self):
"""
The request is cancelled on behalf of the sender.
"""
self.is_active = False
self.save()
from rest_framework import serializers
from .models import FriendList, FriendRequest
class FriendListSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.ReadOnlyField(source="user")
class Meta:
model = FriendList
fields = [
"url",
"id",
"user",
"friends",
]
class FriendRequestSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name="friends:friendrequest-detail")
class Meta:
model = FriendRequest
fields = [
"url",
"id",
"sender",
"receiver",
"is_active",
"timestamp",
]
from django.test import TestCase
# Create your tests here.
from django.urls import path
from rest_framework import routers
from .views import friend_list, friend_detail, FriendRequestViewSet
app_name = "friends"
router = routers.SimpleRouter()
router.register(r'friend-requests', FriendRequestViewSet)
urlpatterns = [
path('friend_list/', friend_list, name='friend-list'),
path('friend_detail/<int:pk>/', friend_detail, name='friend-detail'),
]
urlpatterns += router.urls
from django.db.models.query_utils import Q
from rest_framework import viewsets
from rest_framework.decorators import api_view, action
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.response import Response
from rest_framework.status import HTTP_400_BAD_REQUEST, HTTP_401_UNAUTHORIZED, HTTP_201_CREATED
from users.models import User
from friends.models import FriendRequest, FriendList
from friends.serializers import FriendListSerializer, FriendRequestSerializer
from users.serializers import UserSerializer
@api_view(['GET'])
def friend_list(request):
friends = FriendList.objects.all()
serializer = FriendListSerializer(friends, many=True)
return Response(serializer.data)
@api_view(['GET'])
def friend_detail(request, pk):
friends = FriendList.objects.get(id=pk)
serializer = FriendListSerializer(friends, many=False)
return Response(serializer.data)
"""
@api_view(['GET'])
def is_friend(request, pk):
"""
class FriendRequestViewSet(viewsets.ModelViewSet):
queryset = FriendRequest.objects.all()
serializer_class = FriendRequestSerializer
lookup_field_options = ["sender", "receiver"]
def create(self, request, *args, **kwargs):
data = {"receiver": request.data["receiver"]}
serialized_sender = UserSerializer(request.user, context={'request': request})
data['sender'] = serialized_sender.data['url']
serializer = self.serializer_class(data=data, context={"request": request})
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
def get_object(self):
req_receiver = None
req_sender = None
if "receiver" in self.request.data:
req_receiver = self.request.data["receiver"].split("/")[-2]
if "sender" in self.request.data:
req_sender = self.request.data["sender"].split("/")[-2]
qs = FriendRequest.objects.filter(
(Q(sender=self.request.user) & Q(receiver=req_receiver))
| (Q(receiver=self.request.user) & Q(sender=req_sender))
)
return qs.first()
@action(detail=True, methods=['patch'])
def accept_request(self, request):
friend_request = self.get_object()
data = {"sender": request.data["sender"]}
serialized_receiver = UserSerializer(request.user, context={'request': request})
data['receiver'] = serialized_receiver.data['url']
serializer = self.serializer_class(data=data, context={"request": request})
if serializer.is_valid(raise_exception=True):
if request.user.is_authenticated and request.user == friend_request.receiver:
friend_request.accept()
friend_request.save()
return Response({'status': 'Friend request was accepted.'})
else:
Response(AuthenticationFailed.default_detail, status=HTTP_401_UNAUTHORIZED)
else:
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
@action(detail=True, methods=['patch'])
def decline_request(self, request, pk=None):
friend_request = self.get_object()
serializer = FriendListSerializer(data=request.data)
if serializer.is_valid():
if request.user.is_authenticated and request.user == friend_request.receiver:
friend_request.decline()
friend_request.save()
return Response({'status': 'Friend request was declined.'})
else:
Response(AuthenticationFailed.default_detail, status=HTTP_401_UNAUTHORIZED)
else:
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
@action(detail=True, methods=['delete'])
def cancel_request(self, request, pk=None):
friend_request = self.get_object()
serializer = FriendListSerializer(data=request.data)
if serializer.is_valid():
if request.user.is_authenticated and request.user == friend_request.sender:
friend_request.cancel()
friend_request.save()
return Response({'status': 'Friend request was cancelled.'})
else:
Response(AuthenticationFailed.default_detail, status=HTTP_401_UNAUTHORIZED)
else:
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
import os
import dj_database_url
from django.test.runner import DiscoverRunner
MAX_CONN_AGE = 600
......
......@@ -10,10 +10,10 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""
from pathlib import Path
import os
from secfit.djangoHeroku import settings
from pathlib import Path
from secfit.djangoHeroku import settings
# Get the GROUPID variable to accept connections from the application server and NGINX
groupid = os.environ.get("GROUPID", "0")
......@@ -58,7 +58,6 @@ INSTALLED_APPS = [
"django.contrib.staticfiles",
"rest_framework",