Skip to content
Snippets Groups Projects
Verified Commit acb3db51 authored by Jan Maximilian Michal's avatar Jan Maximilian Michal
Browse files

Subscriptions now always contain an assignment

* on creation or deletation of an assignment of the subscription
  a new submission is looked up. Therefore, currently deleting an
  assignment may leads to SubscriptionEnded exception.
* Cleaned up the urls config
parent 83a79648
No related branches found
No related tags found
No related merge requests found
Pipeline #
......@@ -44,12 +44,15 @@ test_pytest:
artifacts:
paths:
- .coverage
cache:
paths:
- .coverage
test_flake8:
<<: *test_definition_virtualenv
stage: test
script:
- flake8 --exclude=migrations --ignore=N802 core
- flake8 --exclude=migrations --ignore=N802 core util/factories.py
# ----------------------------- Frontend subsection -------------------------- #
.test_template_frontend: &test_definition_frontend
......
from django.core.management.base import BaseCommand
from util.factories import init_test_instance
class Command(BaseCommand):
help = 'Creates some initial test data for the application'
def handle(self, *args, **options):
init_test_instance()
......@@ -508,12 +508,15 @@ class GeneralTaskSubscription(models.Model):
Q(feedback__isnull=True)
)
log.debug('unassigned notfinal submissions %s',
log.debug('unassigned non final submissions %s',
unassigned_non_final_submissions)
return unassigned_non_final_submissions
def _find_unassigned_generated_non_final_submissions(self):
if self._get_submission_base_query().empty():
return ()
raise NotImplementedError
generated_not_final_submissions = \
......@@ -540,14 +543,23 @@ class GeneralTaskSubscription(models.Model):
raise SubscriptionEnded(
f'The task which user {self.owner} subscribed to is done')
@transaction.atomic
def get_or_create_work_assignment(self):
task = self._get_next_assignment_in_subscription()
return TutorSubmissionAssignment.objects.get_or_create(
log.debug(task)
return TutorSubmissionAssignment.objects.create(
subscription=self,
submission=task)
def create_new_assignment_if_subscription_empty(self):
if self.assignments.count() == 0:
self.get_or_create_work_assignment()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.create_new_assignment_if_subscription_empty()
class TutorSubmissionAssignment(models.Model):
......@@ -572,23 +584,9 @@ class TutorSubmissionAssignment(models.Model):
super().save(*args, **kwargs)
@transaction.atomic
def start(self):
if not hasattr(self.submission, 'feedback'):
self.submission.feedback = Feedback()
self.submission.feedback.save()
self.active = True
self.save()
@transaction.atomic
def finish(self):
# TODO think of constraints that forbin finishing
self.delete()
@transaction.atomic
def hold(self):
self.active = False
self.save()
def delete(self, *args, **kwargs):
super().delete(*args, **kwargs)
self.subscription.create_new_assignment_if_subscription_empty()
def __str__(self):
return (f'{self.assignee} assigned to {self.submission}'
......
import logging
from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
from drf_dynamic_fields import DynamicFieldsMixin
from rest_framework import serializers
......@@ -101,19 +102,11 @@ class TutorSerializer(DynamicFieldsModelSerializer):
class AssignmentSerializer(DynamicFieldsModelSerializer):
subscription_id = serializers.CharField()
active = serializers.ReadOnlyField()
def create(self, validated_data):
subscription = GeneralTaskSubscription.objects.get(
subscription_id=validated_data['subscription_id'])
assignment, _ = subscription.get_or_create_work_assignment()
return assignment
class Meta:
model = TutorSubmissionAssignment
fields = ('assignment_id', 'subscription_id', 'submission', 'active')
read_only_fields = ('submission',)
fields = ('assignment_id', 'active',)
read_only_fields = ('assignment_id', 'active',)
class SubscriptionSerializer(DynamicFieldsModelSerializer):
......@@ -128,6 +121,11 @@ class SubscriptionSerializer(DynamicFieldsModelSerializer):
f'The {data["query_type"]} query_type does not work with the'
f'provided key')
try:
get_user_model().objects.get(username=data['owner']['username'])
except ObjectDoesNotExist as err:
raise serializers.ValidationError(str(err))
# Todo more validators especially regarding unique or
# mandatory key field
......@@ -138,8 +136,7 @@ class SubscriptionSerializer(DynamicFieldsModelSerializer):
get_user_model().objects.get(
username=validated_data['owner']['username'])
return GeneralTaskSubscription.objects.create(
**validated_data)
return GeneralTaskSubscription.objects.create(**validated_data)
class Meta:
model = GeneralTaskSubscription
......
......@@ -2,7 +2,7 @@
from django.test import TestCase
from core.models import (GeneralTaskSubscription, Submission, SubmissionType,
TutorSubmissionAssignment)
SubscriptionEnded)
from util.factories import GradyUserFactory
......@@ -16,63 +16,28 @@ class GeneralTaskSubscriptionRandomTest(TestCase):
self.t = self.user_factory.make_tutor()
self.s1 = self.user_factory.make_student()
self.s2 = self.user_factory.make_student()
self.subscription = GeneralTaskSubscription.objects.create(
owner=self.t.user, query_type=GeneralTaskSubscription.RANDOM)
self.submission_type = SubmissionType.objects.create(
name='submission_01', full_score=14
)
name='submission_01', full_score=14)
self.submission_01 = Submission.objects.create(
type=self.submission_type, student=self.s1, text='I really failed')
self.submission_02 = Submission.objects.create(
type=self.submission_type, student=self.s2, text='I like apples')
self.work, _ = self.subscription.get_or_create_work_assignment()
def test_first_work_assignment_was_created_inactive(self):
self.assertFalse(self.work.active)
def test_cannot_get_new_assignment_when_previous_unfinished(self):
_, created = self.subscription.get_or_create_work_assignment()
self.assertFalse(created)
class AssignmentTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.user_factory = GradyUserFactory()
def setUp(self):
self.t = self.user_factory.make_tutor()
self.s = self.user_factory.make_student()
self.subscription = GeneralTaskSubscription.objects.create(
owner=self.t.user, query_type=GeneralTaskSubscription.RANDOM)
self.submission_type = SubmissionType.objects.create(
name='submission_01', full_score=14
)
self.submission_01 = Submission.objects.create(
type=self.submission_type, student=self.s, text='I really failed')
self.work, _ = self.subscription.get_or_create_work_assignment()
def test_subscription_gets_an_assignment(self):
self.assertEqual(1, self.subscription.assignments.count())
def test_can_start_assignment(self):
self.work.start()
self.assertTrue(self.work.active)
def test_feedback_was_created(self):
self.work.start()
self.assertTrue(hasattr(self.work.submission, 'feedback'))
def test_first_work_assignment_was_created_inactive(self):
self.assertFalse(self.subscription.assignments.first().active)
def test_stop(self):
self.work.start()
self.work.finish()
self.assertEqual(0, TutorSubmissionAssignment.objects.count())
def test_subscription_gets_renewed_when_assignment_is_deleted(self):
self.subscription.assignments.first().delete()
self.assertEqual(1, self.subscription.assignments.count())
def test_hold(self):
self.work.start()
self.work.hold()
self.assertFalse(self.work.active)
def test_subscription_raises_error_when_depleted(self):
self.subscription.assignments.first().delete()
self.assertRaises(SubscriptionEnded,
self.subscription.assignments.first().delete())
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import include, path
from django.views.generic.base import TemplateView
from django.urls import path
from rest_framework.routers import DefaultRouter
from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token
from core import views
......@@ -27,11 +25,7 @@ regular_views_urlpatterns = [
]
urlpatterns = [
path('api/', include(router.urls)),
path('api/', include(regular_views_urlpatterns)),
path('api-token-auth/', obtain_jwt_token),
path('api-token-refresh/', refresh_jwt_token),
path('', TemplateView.as_view(template_name='index.html')),
*router.urls,
*regular_views_urlpatterns,
*staticfiles_urlpatterns()
]
urlpatterns += staticfiles_urlpatterns()
from django.contrib import admin
from django.urls import include, path
from django.views.generic.base import TemplateView
from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('core.urls')),
path('api-auth/', include('rest_framework.urls',
namespace='rest_framework')),
path('', include('core.urls')),
path('api-token-auth/', obtain_jwt_token),
path('api-token-refresh/', refresh_jwt_token),
path('', TemplateView.as_view(template_name='index.html'))
]
......@@ -227,7 +227,7 @@ def init_test_instance():
}],
'reviewers': [{
'username': 'reviewer01',
'password': 'pass'
'password': 'p'
}],
'submissions': [
{
......@@ -283,7 +283,7 @@ def init_test_instance():
' asasxasx\n'
' lorem ipsum und so\n',
'type': '03. This one exists for the sole purpose to test',
'user': 'student02',
'user': 'student02'
},
]}
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment