Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Submission logic #107

Merged
merged 17 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions .env

This file was deleted.

3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ data/*
data/nginx/ssl/*
data/postres*
data/redis/*
backend/data/production/*

/node_modules
backend/staticfiles/*

!data/nginx/ssl/.gitkeep
!data/production/*
!data/testing/*
4 changes: 2 additions & 2 deletions backend/api/serializers/checks_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ class StructureCheckSerializer(serializers.ModelSerializer):
read_only=True
)

obligated_extensions = FileExtensionSerializer(many=True)
obligated_extensions = FileExtensionSerializer(many=True, required=False, default=[], read_only=True)

blocked_extensions = FileExtensionSerializer(many=True)
blocked_extensions = FileExtensionSerializer(many=True, required=False, default=[], read_only=True)

class Meta:
model = StructureCheck
Expand Down
31 changes: 31 additions & 0 deletions backend/api/serializers/project_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
from rest_framework.exceptions import ValidationError
from django.utils import timezone
from api.models.submission import Submission, SubmissionFile
from api.models.checks import FileExtension, StructureCheck
from api.serializers.submission_serializer import SubmissionSerializer
from api.serializers.checks_serializer import StructureCheckSerializer
from rest_framework.request import Request


class ProjectSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -81,6 +84,7 @@ class SubmissionStatusSerializer(serializers.Serializer):

class SubmissionAddSerializer(SubmissionSerializer):
def validate(self, data):

group: Group = self.context["group"]
project: Project = group.project

Expand All @@ -95,3 +99,30 @@ def validate(self, data):
raise ValidationError(gettext("project.error.submissions.archived_project"))

return data


class StructureCheckAddSerializer(StructureCheckSerializer):
def validate(self, data):
project: Project = self.context["project"]
if project.structure_checks.filter(name=data["name"]).count():
raise ValidationError(gettext("project.error.structure_checks.already_existing"))

obl_ext = set()
for ext in self.context["obligated"]:
extensie, _ = FileExtension.objects.get_or_create(
extension=ext
)
obl_ext.add(extensie)
data["obligated_extensions"] = obl_ext

block_ext = set()
for ext in self.context["blocked"]:
extensie, _ = FileExtension.objects.get_or_create(
extension=ext
)
if extensie in obl_ext:
raise ValidationError(gettext("project.error.structure_checks.extension_blocked_and_obligated"))
block_ext.add(extensie)
data["blocked_extensions"] = block_ext

return data
2 changes: 1 addition & 1 deletion backend/api/serializers/submission_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def create(self, validated_data):
if not status:
pas = False

# Set structure_checks_passed to True
# Set structure_checks_passed
submission.structure_checks_passed = pas
submission.save()
return submission
178 changes: 171 additions & 7 deletions backend/api/tests/test_submission.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from django.utils.translation import gettext
from datetime import timedelta
from django.utils import timezone
from django.urls import reverse
Expand All @@ -10,15 +11,17 @@
from api.models.course import Course
from api.models.checks import ExtraCheck
from django.conf import settings
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db.models import Max


def create_course(name, academic_startyear, description=None, parent_course=None):
def create_course(name, academic_start_year, description=None, parent_course=None):
"""
Create a Course with the given arguments.
"""
return Course.objects.create(
name=name,
academic_startyear=academic_startyear,
academic_startyear=academic_start_year,
description=description,
parent_course=parent_course,
)
Expand All @@ -32,6 +35,15 @@ def create_project(name, description, days, course):
)


def create_past_project(name, description, days, course, days_start_date):
"""Create a Project with the given arguments."""
deadline = timezone.now() + timedelta(days=days)
startDate = timezone.now() + timedelta(days=days_start_date)
return Project.objects.create(
name=name, description=description, deadline=deadline, course=course, score_visible=True, start_date=startDate
)


def create_group(project, score):
"""Create a Group with the given arguments."""
return Group.objects.create(project=project, score=score)
Expand Down Expand Up @@ -74,7 +86,7 @@ def test_submission_exists(self):
"""
Able to retrieve a single submission after creating it.
"""
course = create_course(name="sel2", academic_startyear=2023)
course = create_course(name="sel2", academic_start_year=2023)
project = create_project(
name="Project 1", description="Description 1", days=7, course=course
)
Expand Down Expand Up @@ -113,7 +125,7 @@ def test_multiple_submission_exists(self):
"""
Able to retrieve multiple submissions after creating them.
"""
course = create_course(name="sel2", academic_startyear=2023)
course = create_course(name="sel2", academic_start_year=2023)
project = create_project(
name="Project 1", description="Description 1", days=7, course=course
)
Expand Down Expand Up @@ -165,7 +177,7 @@ def test_submission_detail_view(self):
"""
Able to retrieve details of a single submission.
"""
course = create_course(name="sel2", academic_startyear=2023)
course = create_course(name="sel2", academic_start_year=2023)
project = create_project(
name="Project 1", description="Description 1", days=7, course=course
)
Expand Down Expand Up @@ -202,7 +214,7 @@ def test_submission_group(self):
"""
Able to retrieve group of a single submission.
"""
course = create_course(name="sel2", academic_startyear=2023)
course = create_course(name="sel2", academic_start_year=2023)
project = create_project(
name="Project 1", description="Description 1", days=7, course=course
)
Expand Down Expand Up @@ -254,7 +266,7 @@ def test_submission_extra_checks(self):
"""
Able to retrieve extra checks of a single submission.
"""
course = create_course(name="sel2", academic_startyear=2023)
course = create_course(name="sel2", academic_start_year=2023)
project = create_project(
name="Project 1", description="Description 1", days=7, course=course
)
Expand Down Expand Up @@ -292,3 +304,155 @@ def test_submission_extra_checks(self):
self.assertEqual(
retrieved_extra_check["passed"], extra_check_result.passed
)

def test_submission_before_deadline(self):
"""
Able to subbmit to a project before the deadline.
"""
zip_file_path = "data/testing/tests/mixed.zip"

with open(zip_file_path, 'rb') as file:
files = {'files': SimpleUploadedFile('mixed.zip', file.read())}
course = create_course(name="sel2", academic_start_year=2023)
project = create_project(
name="Project 1", description="Description 1", days=7, course=course
)
group = create_group(project=project, score=10)

response = self.client.post(
reverse("group-submissions", args=[str(group.id)]),
files,
follow=True,
)

self.assertEqual(response.status_code, 200)
self.assertEqual(response.accepted_media_type, "application/json")
self.assertEqual(json.loads(response.content), {"message": gettext("group.success.submissions.add")})

def test_submission_after_deadline(self):
"""
Not able to subbmit to a project after the deadline.
"""
zip_file_path = "data/testing/tests/mixed.zip"

with open(zip_file_path, 'rb') as f:
files = {'files': SimpleUploadedFile('mixed.zip', f.read())}

course = create_course(name="sel2", academic_start_year=2023)
project = create_past_project(
name="Project 1", description="Description 1", days=-7, course=course, days_start_date=-84
)

group = create_group(project=project, score=10)

response = self.client.post(
reverse("group-submissions", args=[str(group.id)]),
files,
follow=True,
)

self.assertEqual(response.status_code, 400)
self.assertEqual(response.accepted_media_type, "application/json")
self.assertEqual(json.loads(response.content), {
'non_field_errors': [gettext("project.error.submissions.past_project")]})

def test_submission_number_increases_by_1(self):
"""
When submiting a submission the submission number should be the prev one + 1
"""
zip_file_path = "data/testing/tests/mixed.zip"

with open(zip_file_path, 'rb') as f:
files = {'files': SimpleUploadedFile('mixed.zip', f.read())}

course = create_course(name="sel2", academic_start_year=2023)
project = create_project(
name="Project 1", description="Description 1", days=7, course=course
)
group = create_group(project=project, score=10)

max_submission_number_before = group.submissions.aggregate(Max('submission_number'))['submission_number__max']

if max_submission_number_before is None:
max_submission_number_before = 0

old_submissions = group.submissions.count()
response = self.client.post(
reverse("group-submissions", args=[str(group.id)]),
files,
follow=True,
)

group.refresh_from_db()
new_submissions = group.submissions.count()

max_submission_number_after = group.submissions.aggregate(Max('submission_number'))['submission_number__max']

if max_submission_number_after is None:
max_submission_number_after = 0
self.assertEqual(max_submission_number_after - max_submission_number_before, 1)
self.assertEqual(new_submissions - old_submissions, 1)

self.assertEqual(response.status_code, 200)
self.assertEqual(response.accepted_media_type, "application/json")
self.assertEqual(json.loads(response.content), {"message": gettext("group.success.submissions.add")})

def test_submission_invisible_project(self):
"""
Not able to subbmit to a project if its not visible.
"""
zip_file_path = "data/testing/tests/mixed.zip"

with open(zip_file_path, 'rb') as f:
files = {'files': SimpleUploadedFile('mixed.zip', f.read())}

course = create_course(name="sel2", academic_start_year=2023)
project = create_project(
name="Project 1", description="Description 1", days=7, course=course
)

project.toggle_visible()
project.save()

group = create_group(project=project, score=10)

response = self.client.post(
reverse("group-submissions", args=[str(group.id)]),
files,
follow=True,
)

self.assertEqual(response.status_code, 400)
self.assertEqual(response.accepted_media_type, "application/json")
self.assertEqual(json.loads(response.content), {
'non_field_errors': [gettext("project.error.submissions.non_visible_project")]})

def test_submission_archived_project(self):
"""
Not able to subbmit to a project if its archived.
"""
zip_file_path = "data/testing/tests/mixed.zip"

with open(zip_file_path, 'rb') as f:
files = {'files': SimpleUploadedFile('mixed.zip', f.read())}

course = create_course(name="sel2", academic_start_year=2023)
project = create_project(
name="Project 1", description="Description 1", days=7, course=course
)

project.toggle_archived()
project.save()

group = create_group(project=project, score=10)

response = self.client.post(
reverse("group-submissions", args=[str(group.id)]),
files,
follow=True,
)

self.assertEqual(response.status_code, 400)
self.assertEqual(response.accepted_media_type, "application/json")
self.assertEqual(json.loads(response.content), {
'non_field_errors': [gettext("project.error.submissions.archived_project")]})
33 changes: 32 additions & 1 deletion backend/api/views/project_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@
from api.models.group import Group
from api.models.submission import Submission
from api.models.project import Project
from api.models.checks import StructureCheck
from api.serializers.checks_serializer import StructureCheckSerializer, ExtraCheckSerializer
from api.serializers.project_serializer import ProjectSerializer, TeacherCreateGroupSerializer, SubmissionStatusSerializer
from api.serializers.project_serializer import (
StructureCheckAddSerializer, SubmissionStatusSerializer,
ProjectSerializer, TeacherCreateGroupSerializer
)

from api.serializers.group_serializer import GroupSerializer
from api.serializers.submission_serializer import SubmissionSerializer
from rest_framework.request import Request


class ProjectViewSet(CreateModelMixin,
Expand Down Expand Up @@ -88,6 +94,31 @@ def structure_checks(self, request, **_):
)
return Response(serializer.data)

@structure_checks.mapping.post
@structure_checks.mapping.put
def _add_structure_check(self, request: Request, **_):
"""Add an structure_check to the project"""

project: Project = self.get_object()

# Add submission to course
serializer = StructureCheckAddSerializer(
data=request.data,
context={
"project": project,
"request": request,
"obligated": request.data.getlist('obligated_extensions'),
"blocked": request.data.getlist('blocked_extensions')
}
)

if serializer.is_valid(raise_exception=True):
serializer.save(project=project)

return Response({
"message": gettext("project.success.structure_check.add")
})

@action(detail=True, methods=["get"])
def extra_checks(self, request, **_):
"""Returns the extra checks for the given project"""
Expand Down
File renamed without changes.
Loading
Loading