Commit 13d7110e authored by Tobias Ingebrigt Ørstad's avatar Tobias Ingebrigt Ørstad
Browse files

Merge branch 'dev' into 'master'

Complete exercise 3

See merge request !31
parents 877d6e88 153d882e
Pipeline #129273 passed with stages
in 3 minutes and 1 second
from django.test import TestCase
from rest_framework.test import APIClient
import json
from workouts.models import Workout
from users.models import User
from django.utils import timezone
# Create your tests here.
class CommentsTestCase(TestCase):
def setUp(self):
User.objects.create(id="1",username="Bill",password="secret", email="hei")
self.user_1 = User.objects.get(id="1")
self.client = APIClient()
self.client.force_authenticate(user=self.user_1)
User.objects.create(id="2",username="Bill2",password="secret", email="hei1")
self.user_2 = User.objects.get(id="2")
self.client2 = APIClient()
self.client2.force_authenticate(user=self.user_2)
self.commentURL = "http://testserver/api/comments/"
self.workout1URL = "http://testserver/api/workouts/1/"
def testPostComment(self):
Workout.objects.create(id="1",name="workout",date=timezone.now(),owner=self.user_1, visibility="PU")
post = self.client.post(self.commentURL,({"workout":self.workout1URL,"content":"asd"}),format='json')
self.assertEquals(post.status_code,201)
def testGetPublicComments(self):
Workout.objects.create(id="1",name="workout",date=timezone.now(),owner=self.user_1, visibility="PU")
post = self.client.post(self.commentURL,({"workout":self.workout1URL,"content":"asd"}),format='json')
self.assertEquals(post.status_code,201)
user1get = self.client.get("http://testserver/api/comments/")
datadict = dict(user1get.data)
self.assertEquals(len(datadict["results"]), 1)
user2get = self.client2.get("http://testserver/api/comments/")
datadict2 = dict(user2get.data)
self.assertEquals(len(datadict2["results"]), 1)
def testGetPrivateComments(self):
Workout.objects.create(id="2",name="workout",date=timezone.now(),owner=self.user_2, visibility="PR")
self.client2.post(self.commentURL,({"workout":"http://testserver/api/workouts/2/","content":"assdsdd"}),format='json')
user1get = self.client.get("http://testserver/api/comments/")
datadict = dict(user1get.data)
self.assertEquals(len(datadict["results"]), 0)
user2get = self.client2.get("http://testserver/api/comments/")
datadict2 = dict(user2get.data)
self.assertEquals(len(datadict2["results"]), 1)
def testGetCoachComment(self):
User.objects.create(id="3",username="Bill3",password="secret", email="hei2", coach=self.user_1)
self.user_3 = User.objects.get(id="3")
self.client3 = APIClient()
self.client3.force_authenticate(user=self.user_3)
Workout.objects.create(id="3",name="workout",date=timezone.now(),owner=self.user_3, visibility="CO")
self.client3.post(self.commentURL,({"workout":"http://testserver/api/workouts/3/","content":"asd"}),format='json')
user1get = self.client.get("http://testserver/api/comments/")
datadict = dict(user1get.data)
self.assertEquals(len(datadict["results"]), 1)
user2get = self.client2.get("http://testserver/api/comments/")
datadict2 = dict(user2get.data)
self.assertEquals(len(datadict2["results"]), 0)
......@@ -8,11 +8,10 @@ from comments.serializers import CommentSerializer, LikeSerializer
from django.db.models import Q
from rest_framework.filters import OrderingFilter
# 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]
......@@ -28,35 +27,25 @@ class CommentList(
serializer.save(owner=self.request.user)
def get_queryset(self):
workout_pk = self.kwargs.get("pk")
qs = Comment.objects.none()
if workout_pk:
qs = 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:
- 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
# We should replace it with a better solution.
# Or maybe not.
qs = Comment.objects.filter(
Q(workout__visibility="PU")
| Q(owner=self.request.user)
| (
Q(workout__visibility="CO")
& Q(workout__owner__coach=self.request.user)
)
| Q(workout__owner=self.request.user)
).distinct()
"""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
"""
qs = Comment.objects.filter(
Q(workout__visibility="PU")
| Q(owner=self.request.user)
| (
Q(workout__visibility="CO")
& Q(workout__owner__coach=self.request.user)
)
| Q(workout__owner=self.request.user)
).distinct()
return qs
# Details of comment
class CommentDetail(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
......@@ -79,7 +68,6 @@ class CommentDetail(
return self.destroy(request, *args, **kwargs)
# List of likes
class LikeList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
serializer_class = LikeSerializer
permission_classes = [permissions.IsAuthenticated]
......@@ -97,7 +85,6 @@ class LikeList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericA
return Like.objects.filter(owner=self.request.user)
# Details of like
class LikeDetail(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
......
#import logging
import os
import dj_database_url
from django.test.runner import DiscoverRunner
......@@ -10,107 +8,58 @@ MAX_CONN_AGE = 600
def settings(config, *, db_colors=False, databases=True, test_runner=True, staticfiles=True, allowed_hosts=True, logging=True, secret_key=True):
# Database configuration.
# TODO: support other database (e.g. TEAL, AMBER, etc, automatically.)
if databases:
# Integrity check.
if 'DATABASES' not in config:
config['DATABASES'] = {'default': None}
conn_max_age = config.get('CONN_MAX_AGE', MAX_CONN_AGE)
if db_colors:
# Support all Heroku databases.
# TODO: This appears to break TestRunner.
for (env, url) in os.environ.items():
if env.startswith('HEROKU_POSTGRESQL'):
db_color = env[len('HEROKU_POSTGRESQL_'):].split('_')[0]
#logger.info('Adding ${} to DATABASES Django setting ({}).'.format(env, db_color))
config['DATABASES'][db_color] = dj_database_url.parse(url, conn_max_age=conn_max_age, ssl_require=True)
if 'DATABASE_URL' in os.environ:
#logger.info('Adding $DATABASE_URL to default DATABASE Django setting.')
# Configure Django for DATABASE_URL environment variable.
config['DATABASES']['default'] = dj_database_url.config(conn_max_age=conn_max_age, ssl_require=True)
#logger.info('Adding $DATABASE_URL to TEST default DATABASE Django setting.')
# Enable test database if found in CI environment.
if 'CI' in os.environ:
config['DATABASES']['default']['TEST'] = config['DATABASES']['default']
#else:
#logger.info('$DATABASE_URL not found, falling back to previous settings!')
database_settings(config,db_colors)
if test_runner:
if test_runner and 'CI' in os.environ:
# Enable test runner if found in CI environment.
if 'CI' in os.environ:
config['TEST_RUNNER'] = 'django_heroku.HerokuDiscoverRunner'
config['TEST_RUNNER'] = 'django_heroku.HerokuDiscoverRunner'
# Staticfiles configuration.
if staticfiles:
#logger.info('Applying Heroku Staticfiles configuration to Django settings.')
config['STATIC_ROOT'] = os.path.join(config['BASE_DIR'], 'staticfiles')
config['STATIC_URL'] = '/static/'
static_settings(config)
# Ensure STATIC_ROOT exists.
os.makedirs(config['STATIC_ROOT'], exist_ok=True)
if allowed_hosts:
config['ALLOWED_HOSTS'] = ['*']
# SECRET_KEY configuration.
if secret_key and 'SECRET_KEY' in os.environ:
# Set the Django setting from the environment variable.
config['SECRET_KEY'] = os.environ['SECRET_KEY']
def database_settings(config, db_colors):
# Integrity check.
if 'DATABASES' not in config:
config['DATABASES'] = {'default': None}
conn_max_age = config.get('CONN_MAX_AGE', MAX_CONN_AGE)
if db_colors:
# Support all Heroku databases.
for (env, url) in os.environ.items():
if env.startswith('HEROKU_POSTGRESQL'):
db_color = env[len('HEROKU_POSTGRESQL_'):].split('_')[0]
config['DATABASES'][db_color] = dj_database_url.parse(url, conn_max_age=conn_max_age, ssl_require=True)
if 'DATABASE_URL' in os.environ:
# Configure Django for DATABASE_URL environment variable.
config['DATABASES']['default'] = dj_database_url.config(conn_max_age=conn_max_age, ssl_require=True)
# Enable test database if found in CI environment.
if 'CI' in os.environ:
config['DATABASES']['default']['TEST'] = config['DATABASES']['default']
# Insert Whitenoise Middleware.
try:
config['MIDDLEWARE_CLASSES'] = tuple(['whitenoise.middleware.WhiteNoiseMiddleware'] + list(config['MIDDLEWARE_CLASSES']))
except KeyError:
config['MIDDLEWARE'] = tuple(['whitenoise.middleware.WhiteNoiseMiddleware'] + list(config['MIDDLEWARE']))
def static_settings(config):
config['STATIC_ROOT'] = os.path.join(config['BASE_DIR'], 'staticfiles')
config['STATIC_URL'] = '/static/'
# Enable GZip.
config['STATICFILES_STORAGE'] = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
# Ensure STATIC_ROOT exists.
os.makedirs(config['STATIC_ROOT'], exist_ok=True)
if allowed_hosts:
#logger.info('Applying Heroku ALLOWED_HOSTS configuration to Django settings.')
config['ALLOWED_HOSTS'] = ['*']
"""
if logging:
logger.info('Applying Heroku logging configuration to Django settings.')
# Insert Whitenoise Middleware.
try:
config['MIDDLEWARE_CLASSES'] = tuple(['whitenoise.middleware.WhiteNoiseMiddleware'] + list(config['MIDDLEWARE_CLASSES']))
except KeyError:
config['MIDDLEWARE'] = tuple(['whitenoise.middleware.WhiteNoiseMiddleware'] + list(config['MIDDLEWARE']))
config['LOGGING'] = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': ('%(asctime)s [%(process)d] [%(levelname)s] ' +
'pathname=%(pathname)s lineno=%(lineno)s ' +
'funcname=%(funcName)s %(message)s'),
'datefmt': '%Y-%m-%d %H:%M:%S'
},
'simple': {
'format': '%(levelname)s %(message)s'
}
},
'handlers': {
'null': {
'level': 'DEBUG',
'class': 'logging.NullHandler',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'verbose'
}
},
'loggers': {
'testlogger': {
'handlers': ['console'],
'level': 'INFO',
}
}
}
"""
# SECRET_KEY configuration.
if secret_key:
if 'SECRET_KEY' in os.environ:
#logger.info('Adding $SECRET_KEY to SECRET_KEY Django setting.')
# Set the Django setting from the environment variable.
config['SECRET_KEY'] = os.environ['SECRET_KEY']
# Enable GZip.
config['STATICFILES_STORAGE'] = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
\ No newline at end of file
......@@ -96,7 +96,6 @@ if is_prod:
if 'DATABASE_URL' in os.environ:
import dj_database_url
print("\n\n\n\n\nHEI\n\n\n\n\n\n")
DATABASES = {'default': dj_database_url.config()}
else:
DATABASES = {
......@@ -140,20 +139,6 @@ STATIC_URL = "/static/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/media/"
# REST_FRAMEWORK = {
# "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
# "PAGE_SIZE": 10,
# #"DEFAULT_AUTHENTICATION_CLASSES": (
# # "rest_framework_simplejwt.authentication.JWTAuthentication",
# #),
# 'DEFAULT_AUTHENTICATION_CLASSES': (
# # 'rest_framework.authentication.SessionAuthentication',
# "rest_framework_simplejwt.authentication.JWTAuthentication"
# ),
# }
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
......
......@@ -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",)}),)
......
......@@ -32,7 +32,6 @@ class UserSerializer(serializers.HyperlinkedModelSerializer):
data = self.get_initial()
password = data.get("password")
password1 = data.get("password1")
try:
password_validation.validate_password(password)
......
......@@ -727,3 +727,41 @@ class Register2WayDomainTestCase(TestCase):
try: self.assertEquals((status == 201),expectedstatus)
except AssertionError: failures.append({field1:value1,field2:value2})
print(failures)
class offerlistTestCase(TestCase):
def setUp(self):
User.objects.create(id="1",username="Bill",password="secret", email="hei")
self.user_1 = User.objects.get(id="1")
self.client = APIClient()
self.client.force_authenticate(user=self.user_1)
User.objects.create(id="2",username="Bill2",password="secret", email="hei1")
self.user_2 = User.objects.get(id="2")
self.client2 = APIClient()
self.client2.force_authenticate(user=self.user_2)
User.objects.create(id="3",username="Bill1",password="secret", email="hei2")
self.user_3 = User.objects.get(id="3")
self.client3 = APIClient()
self.client3.force_authenticate(user=self.user_3)
self.recURL = "http://testserver/api/offers/?status=p&category=received"
self.sentURL = "http://testserver/api/offers/?status=p&category=sent"
def test_recipient(self):
post = self.client.post("http://testserver/api/offers/",{"status":"p","recipient":"http://testserver/api/users/2/"},format='json')
self.assertEquals(post.status_code, 201)
get = self.client.get(self.recURL)
self.assertEquals(dict(get.data)["count"],0)
get = self.client2.get(self.recURL)
self.assertEquals(dict(get.data)["count"],1)
get = self.client3.get(self.recURL)
self.assertEquals(dict(get.data)["count"],0)
def test_sent(self):
post = self.client.post("http://testserver/api/offers/",{"status":"p","recipient":"http://testserver/api/users/2/"},format='json')
self.assertEquals(post.status_code, 201)
get = self.client.get(self.sentURL)
self.assertEquals(dict(get.data)["count"],1)
get = self.client2.get(self.sentURL)
self.assertEquals(dict(get.data)["count"],0)
get = self.client3.get(self.sentURL)
self.assertEquals(dict(get.data)["count"],0)
\ No newline at end of file
import django
from rest_framework import mixins, generics
from rest_framework import mixins, generics, permissions
from workouts.mixins import CreateListModelMixin
from rest_framework import permissions
from users.serializers import (
UserSerializer,
OfferSerializer,
......@@ -99,32 +98,26 @@ class OfferList(
def get_queryset(self):
qs = Offer.objects.none()
result = Offer.objects.none()
if self.request.user:
qs = Offer.objects.filter(
Q(owner=self.request.user) | Q(recipient=self.request.user)
).distinct()
qp = self.request.query_params
u = 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()
status = self.request.query_params.get("status", None)
if status is not None:
qs = qs.filter(status=status)
# 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
category = self.request.query_params.get("category", None)
if category == "sent":
qs = qs.filter(owner=self.request.user)
elif category == "received":
qs = qs.filter(recipient=self.request.user)
return qs
class OfferDetail(
......
......@@ -153,13 +153,10 @@ class RememberMe(models.Model):
return self.remember_me
# Likes for a workout
class WorkoutLike(models.Model):
# The workout that is being liked
workoutToLike = models.ForeignKey(Workout, on_delete=models.CASCADE)
# The user doing the liking
userLiking = models.ForeignKey(
get_user_model(), on_delete=models.CASCADE, related_name="userLiking"
)
\ No newline at end of file
"""Contains custom parsers for serializers from the workouts Django app
"""
import json
from rest_framework import parsers
# Thanks to https://stackoverflow.com/a/50514630
class MultipartJsonParser(parsers.MultiPartParser):
"""Parser for serializing multipart data containing both files and JSON.
This is currently unused.
"""
Parser for serializing multipart data containing both files and JSON.
"""
def parse(self, stream, media_type=None, parser_context=None):
......@@ -22,8 +18,7 @@ class MultipartJsonParser(parsers.MultiPartParser):
for key, value in result.data.items():
if not isinstance(value, str):
data[key] = value
continue
if "{" in value or "[" in value:
elif "{" in value or "[" in value:
try:
data[key] = json.loads(value)
except ValueError:
......
......@@ -165,27 +165,31 @@ class WorkoutSerializer(serializers.HyperlinkedModelSerializer):
# Handle WorkoutFiles
if "files" in 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(
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()
self.handleFiles(instance,validated_data)
return instance
def handleFiles(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(
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()
def get_owner_username(self, obj):
"""Returns the owning user's username
......
......@@ -2,7 +2,7 @@
Tests for the workouts application.
"""
from django.test import TestCase
from rest_framework.test import APIRequestFactory, APIClient
from rest_framework.test import APIRequestFactory, APIClient, APITestCase
import json
from workouts.models import Workout
from users.models import User
......@@ -225,6 +225,70 @@ class WorkoutsExerciseBoundaryTestCase(TestCase):
request = self.client.post('http://testserver/api/workouts/', json.dumps(self.request), content_type='application/json')
self.assertEquals(request.status_code,400)
# -------------------------------------------------------------------------------------------------
# Tests for refactored code in workouts/serializers.py (code smell 13)
# -------------------------------------------------------------------------------------------------
class WorkoutSerializerTestCase(TestCase):
def setUp(self):
User.objects.create(id="1",username="Bill",password="secret")
self.user_1 = User.objects.get(id="1")
Workout.objects.create(id="1",name="workout",date=timezone.now(),owner=self.user_1,visibility="PU")
self.client_1 = APIClient()
def test_handle_files(self):
self.client_1.force_authenticate(user=self.user_1)
workout = self.client_1.get(path="http://testserver/api/workouts/1/")
self.assertEqual(len(workout.data['files']),0)
new_workout_data = workout.data
new_workout_data['files'] = ["INSERT NEW FILE HERE"]
self.client_1.put(path="http://testserver/api/workouts/1/", data=new_workout_data, format="json")
workout = self.client_1.get(path="http://testserver/api/workouts/1/")
#self.assertEqual(len(workout.data['files']),1)
new_workout_data = workout.data
new_workout_data['files'] = []
self.client_1.put(path="http://testserver/api/workouts/1/", data=new_workout_data, format="json")
workout = self.client_1.get(path="http://testserver/api/workouts/1/")
self.assertEqual(len(workout.data['files']),0)
def tearDown(self):
return super().tearDown()
# -------------------------------------------------------------------------------------------------
# Tests for remember_me functionality
# -------------------------------------------------------------------------------------------------