From 6857cadcae5dcb3bc00c60feff4caf5fa8dfb026 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Barnab=C3=A1s=20Cz=C3=A9m=C3=A1n?= <trabarni@gmail.com>
Date: Wed, 4 Apr 2018 09:53:59 +0200
Subject: [PATCH] Fix homework file size validation & created_by field default
 value

---
 src/common/serializers.py                     | 12 +++++
 src/common/validators.py                      | 26 +++++++++++
 src/homework/migrations/0001_initial.py       |  2 +-
 .../migrations/0009_merge_20180404_0825.py    | 14 ++++++
 .../migrations/0010_auto_20180404_0828.py     | 30 ++++++++++++
 .../migrations/0011_auto_20180404_0853.py     | 25 ++++++++++
 src/homework/models.py                        | 46 ++++++++-----------
 src/homework/serializers.py                   | 16 ++-----
 src/homework/views.py                         | 28 +----------
 9 files changed, 135 insertions(+), 64 deletions(-)
 create mode 100644 src/common/serializers.py
 create mode 100644 src/common/validators.py
 create mode 100644 src/homework/migrations/0009_merge_20180404_0825.py
 create mode 100644 src/homework/migrations/0010_auto_20180404_0828.py
 create mode 100644 src/homework/migrations/0011_auto_20180404_0853.py

diff --git a/src/common/serializers.py b/src/common/serializers.py
new file mode 100644
index 0000000..5e08862
--- /dev/null
+++ b/src/common/serializers.py
@@ -0,0 +1,12 @@
+from rest_framework import compat
+
+
+class CurrentUserProfileDefault(object):
+    def set_context(self, serializer_field):
+        self.user = serializer_field.context['request'].user
+
+    def __call__(self):
+        return self.user.profile
+
+    def __repr__(self):
+        return compat.unicode_to_repr('%s()' % self.__class__.__name__)
diff --git a/src/common/validators.py b/src/common/validators.py
new file mode 100644
index 0000000..343f781
--- /dev/null
+++ b/src/common/validators.py
@@ -0,0 +1,26 @@
+from django.core import exceptions
+from django.utils.deconstruct import deconstructible
+from django.utils.translation import ugettext_lazy as _
+
+
+@deconstructible
+class FileSizeValidator():
+    size_limit = 5242880  # 5MB - 5242880
+    message = _('Too big file. %(size)d')
+    code = 'invalid'
+
+    def __init__(self, size_limit=None, message=None, code=None):
+        if size_limit is not None:
+            self.size_limit = size_limit
+        if message is not None:
+            self.message = message
+        if code is not None:
+            self.code = code
+
+    def __call__(self, file):
+        if file.size > self.size_limit:
+            raise exceptions.ValidationError(
+                self.message,
+                code=self.code,
+                params={'size': file.size}
+            )
diff --git a/src/homework/migrations/0001_initial.py b/src/homework/migrations/0001_initial.py
index 57b9c1f..c06eb42 100644
--- a/src/homework/migrations/0001_initial.py
+++ b/src/homework/migrations/0001_initial.py
@@ -42,7 +42,7 @@ class Migration(migrations.Migration):
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('title', models.CharField(max_length=150)),
                 ('date', models.DateTimeField(auto_now_add=True)),
-                ('deadline', models.DateTimeField(validators=[homework.models.validate_deadline])),
+                ('deadline', models.DateTimeField()),
                 ('text', models.TextField()),
                 ('files', models.FileField(blank=True, upload_to='', validators=[django.core.validators.FileExtensionValidator('image/png', 'image/jpeg', 'application/zip')])),
                 ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
diff --git a/src/homework/migrations/0009_merge_20180404_0825.py b/src/homework/migrations/0009_merge_20180404_0825.py
new file mode 100644
index 0000000..f571560
--- /dev/null
+++ b/src/homework/migrations/0009_merge_20180404_0825.py
@@ -0,0 +1,14 @@
+# Generated by Django 2.0.1 on 2018-04-04 06:25
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('homework', '0008_auto_20180221_1135'),
+        ('homework', '0007_merge_20180205_2021'),
+    ]
+
+    operations = [
+    ]
diff --git a/src/homework/migrations/0010_auto_20180404_0828.py b/src/homework/migrations/0010_auto_20180404_0828.py
new file mode 100644
index 0000000..5be35ff
--- /dev/null
+++ b/src/homework/migrations/0010_auto_20180404_0828.py
@@ -0,0 +1,30 @@
+# Generated by Django 2.0.1 on 2018-04-04 06:28
+
+import common.validators
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('homework', '0009_merge_20180404_0825'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='solution',
+            name='files',
+            field=models.FileField(blank=True, upload_to='', validators=[django.core.validators.FileExtensionValidator('image/png', 'image/jpeg', 'application/zip'), common.validators.FileSizeValidator(size_limit=52428800)]),
+        ),
+        migrations.AlterField(
+            model_name='task',
+            name='deadline',
+            field=models.DateTimeField(),
+        ),
+        migrations.AlterField(
+            model_name='task',
+            name='files',
+            field=models.FileField(blank=True, upload_to='', validators=[django.core.validators.FileExtensionValidator('image/png', 'image/jpeg', 'application/zip'), common.validators.FileSizeValidator(size_limit=52428800)]),
+        ),
+    ]
diff --git a/src/homework/migrations/0011_auto_20180404_0853.py b/src/homework/migrations/0011_auto_20180404_0853.py
new file mode 100644
index 0000000..0524fa0
--- /dev/null
+++ b/src/homework/migrations/0011_auto_20180404_0853.py
@@ -0,0 +1,25 @@
+# Generated by Django 2.0.1 on 2018-04-04 06:53
+
+import common.validators
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('homework', '0010_auto_20180404_0828'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='solution',
+            name='files',
+            field=models.FileField(blank=True, upload_to='', validators=[django.core.validators.FileExtensionValidator(['image/png', 'image/jpeg', 'application/zip']), common.validators.FileSizeValidator(size_limit=52428800)]),
+        ),
+        migrations.AlterField(
+            model_name='task',
+            name='files',
+            field=models.FileField(blank=True, upload_to='', validators=[django.core.validators.FileExtensionValidator(['image/png', 'image/jpeg', 'application/zip']), common.validators.FileSizeValidator(size_limit=52428800)]),
+        ),
+    ]
diff --git a/src/homework/models.py b/src/homework/models.py
index 5974d6b..c586fa0 100644
--- a/src/homework/models.py
+++ b/src/homework/models.py
@@ -1,34 +1,25 @@
 from django.db import models
-from django.contrib.auth.models import User
-from django.core.exceptions import ValidationError
-from django.utils.translation import ugettext_lazy as _
-from django.utils import timezone
 from django.core import validators
-from account.models import Profile
-# from . import myfields
-
-
-# 5MB - 5242880
-__MAX_UPLOAD_SIZE = 5242880
 
-
-def validate_deadline(deadline):
-    if deadline <= timezone.now():
-        raise ValidationError(_('Date must be greater than now'), code='invalid')
+from common.validators import FileSizeValidator
+from account.models import Profile
 
 
 class Task(models.Model):
     title = models.CharField(max_length=150)
     date = models.DateTimeField(auto_now_add=True, editable=False)
-    deadline = models.DateTimeField(validators=[validate_deadline])
+    deadline = models.DateTimeField()
     text = models.TextField()
     created_by = models.ForeignKey(Profile, on_delete=models.DO_NOTHING)
     files = models.FileField(
-        validators=[validators.FileExtensionValidator(
-            'image/png',
-            'image/jpeg',
-            'application/zip',
-        )],
+        validators=[
+            validators.FileExtensionValidator([
+                'image/png',
+                'image/jpeg',
+                'application/zip',
+            ]),
+            FileSizeValidator(size_limit=52428800),  # 52428800 - 50MiB
+        ],
         blank=True,
     )
 
@@ -38,15 +29,18 @@ class Task(models.Model):
 
 class Solution(models.Model):
     task = models.ForeignKey(Task, related_name='task_solution', on_delete=models.CASCADE)
-    created_by = models.ForeignKey(Profile, related_name='student_solution',  on_delete=models.CASCADE)
+    created_by = models.ForeignKey(Profile, related_name='student_solution', on_delete=models.CASCADE)
     date = models.DateTimeField(auto_now_add=True, editable=False)
     ready = models.BooleanField(default=False)
     accepted = models.BooleanField(default=False)
     files = models.FileField(
-        validators=[validators.FileExtensionValidator(
-            'image/png',
-            'image/jpeg',
-            'application/zip',
-        )],
+        validators=[
+            validators.FileExtensionValidator([
+                'image/png',
+                'image/jpeg',
+                'zip',
+            ]),
+            FileSizeValidator(size_limit=52428800),  # 52428800 - 50MiB
+        ],
         blank=True,
     )
diff --git a/src/homework/serializers.py b/src/homework/serializers.py
index f3a816e..5b5497f 100644
--- a/src/homework/serializers.py
+++ b/src/homework/serializers.py
@@ -1,29 +1,23 @@
 from rest_framework import serializers
+
+from common.serializers import CurrentUserProfileDefault
 from . import models
 
 
 class TaskSerializer(serializers.ModelSerializer):
+    created_by = serializers.HiddenField(default=CurrentUserProfileDefault())
+
     class Meta:
         model = models.Task
         read_only_fields = ('created_by', 'date')
-        # extra_kwargs = {'created_by': {'default': serializers.CurrentUserDefault()}}
         fields = '__all__'
 
 
 class SolutionSerializer(serializers.ModelSerializer):
     date = serializers.DateTimeField(read_only=True)
+    created_by = serializers.HiddenField(default=CurrentUserProfileDefault())
 
     class Meta:
         model = models.Solution
         read_only_fields = ('created_by', 'date' 'ready')
-        # extra_kwargs = {'created_by': {'default': serializers.CurrentUserDefault()}}
         fields = ('task', 'date', 'accepted', 'files', 'created_by')
-
-    # def validate(self, attrs):
-    #     task = attrs['task']
-    #     date = attrs['date'] keyerror
-    #
-    #     if task.deadline < date:
-    #         raise serializers.ValidationError("You cannot submit homework after the deadline")
-    #
-    #     return attrs
diff --git a/src/homework/views.py b/src/homework/views.py
index c6f1f9b..508a498 100644
--- a/src/homework/views.py
+++ b/src/homework/views.py
@@ -1,15 +1,9 @@
-from rest_framework import viewsets
+from rest_framework import viewsets, status
 from rest_framework.response import Response
-from rest_framework import status
-from rest_framework.decorators import list_route
-from django.http import Http404
-from django.shortcuts import get_object_or_404
-import datetime
-from django.utils import timezone
 
+from common import permissions
 from . import serializers
 from . import models
-from common import permissions
 
 
 class TasksViewSet(viewsets.ModelViewSet):
@@ -22,21 +16,3 @@ class SolutionsViewSet(viewsets.ModelViewSet):
     serializer_class = serializers.SolutionSerializer
     queryset = models.Solution.objects.all()
     permission_classes = (permissions.IsStaffOrReadOnlyForAuthenticated, )
-
-    def perform_create(self, serializer):
-        serializer.validated_data['accepted'] = False
-        task = serializer.validated_data['task']
-        now = timezone.now()
-        if task.deadline < now:
-            raise Http404("Deadline")
-        serializer.save()
-
-    # def get_queryset(self):
-    #     user = self.request.user
-    #     if user.has_perm(permissions.IsStaffUser):
-    #         return models.Solution.objects.all()
-    #
-    # @list_route(methods=['get'])
-    # def me(self, request):
-    #     serializer = self.serializer_class(request.user.profile) #request ?
-    #     return Response(serializer.data)
\ No newline at end of file
-- 
GitLab