diff --git a/core/admin.py b/core/admin.py
index a2e9f295ca1e3cb3e5c3afd5fef9ec0cc66a6443..79001a88ee94f003a380a2964ed9369290d2b476 100644
--- a/core/admin.py
+++ b/core/admin.py
@@ -1,8 +1,8 @@
 from django.contrib import admin
 from django.contrib.auth.models import Group
 
-from core.models import (ExamType, Feedback, Reviewer, Student, Submission,
-                         SubmissionType, Test, Tutor, UserAccount)
+from core.models import (ExamType, Feedback, StudentInfo, Submission,
+                         SubmissionType, Test, UserAccount)
 
 # Stuff we needwant
 admin.site.register(UserAccount)
@@ -11,9 +11,7 @@ admin.site.register(Feedback)
 admin.site.register(Test)
 admin.site.register(ExamType)
 admin.site.register(Submission)
-admin.site.register(Reviewer)
-admin.site.register(Student)
-admin.site.register(Tutor)
+admin.site.register(StudentInfo)
 
 # ... and stuff we don't needwant
 admin.site.unregister(Group)
diff --git a/core/management/commands/extractsubmissions.py b/core/management/commands/extractsubmissions.py
index ac8db1d5ff3c6030d3b874ba0297728e8120fd1d..1e25c4ca4b01970928f13f9e9a8332c007f7e36a 100644
--- a/core/management/commands/extractsubmissions.py
+++ b/core/management/commands/extractsubmissions.py
@@ -1,4 +1,5 @@
 from django.core.management.base import BaseCommand
+
 from core import models
 
 
diff --git a/core/management/commands/replaceusernames.py b/core/management/commands/replaceusernames.py
index d496ee956be59c1baf18601f05f763d07b1069e7..cf1fe74d161ff6355e62f1a3e903de81e44b0676 100644
--- a/core/management/commands/replaceusernames.py
+++ b/core/management/commands/replaceusernames.py
@@ -2,10 +2,9 @@ import argparse
 import json
 import sys
 
+from django.contrib.auth import get_user_model
 from django.core.management.base import BaseCommand
 
-from core.models import Student
-
 
 class Command(BaseCommand):
     help = ('replaces all usernames based on a '
@@ -21,11 +20,11 @@ class Command(BaseCommand):
 
     def _handle(self, matno2username_dict, **kwargs):
         matno2username = json.JSONDecoder().decode(matno2username_dict.read())
-        for student in Student.objects.all():
-            if student.matrikel_no in matno2username:
-                new_name = matno2username[student.matrikel_no]
-                student.user.username = new_name
-                student.user.save()
+        for student in get_user_model().get_students():
+            if student.student.matrikel_no in matno2username:
+                new_name = matno2username[student.student.matrikel_no]
+                student.username = new_name
+                student.save()
 
     def handle(self, *args, **options):
         self._handle(*args, **options)
diff --git a/core/management/commands/setstudentpasswords.py b/core/management/commands/setstudentpasswords.py
index 0b63a275cff529e117ea1de59db82f31d659cab6..0268d6d10d4075b04b1263d0fb32e730e8ff4734 100644
--- a/core/management/commands/setstudentpasswords.py
+++ b/core/management/commands/setstudentpasswords.py
@@ -2,10 +2,9 @@ import csv
 import secrets
 import sys
 
+from django.contrib.auth import get_user_model
 from django.core.management.base import BaseCommand
 
-from core.models import Student
-
 
 class Command(BaseCommand):
     help = ('All student passwords will be changed'
@@ -26,17 +25,17 @@ class Command(BaseCommand):
         writer.writerow(
             ['Name', 'Matrikel', 'Username', 'password', 'instance'])
 
-        for student in Student.objects.all():
+        for student in get_user_model().get_students():
             password = ''.join(secrets.choice(choose_from) for _ in range(3))
 
-            student.user.set_password(password)
-            student.user.save()
+            student.set_password(password)
+            student.save()
 
-            if not student.user.fullname:
-                student.user.fullname = '__no_name__'
+            if not student.fullname:
+                student.fullname = '__no_name__'
 
-            writer.writerow([student.user.fullname, student.matrikel_no,
-                             student.user.username, password, instance])
+            writer.writerow([student.fullname, student.student.matrikel_no,
+                             student.username, password, instance])
 
     def handle(self, *args, **options):
         self._handle(*args, **options)
diff --git a/core/migrations/0006_auto_20180104_2001.py b/core/migrations/0006_auto_20180104_2001.py
index 7a1672479bf09f7629b13877cba53a6183003e49..f790ca9e5be399441ce8ee2176a5b14a27974b82 100644
--- a/core/migrations/0006_auto_20180104_2001.py
+++ b/core/migrations/0006_auto_20180104_2001.py
@@ -1,8 +1,8 @@
 # Generated by Django 2.0.1 on 2018-01-04 20:01
 
+import django.db.models.deletion
 from django.conf import settings
 from django.db import migrations, models
-import django.db.models.deletion
 
 
 class Migration(migrations.Migration):
diff --git a/core/migrations/0007_auto_20180105_1136.py b/core/migrations/0007_auto_20180105_1136.py
new file mode 100644
index 0000000000000000000000000000000000000000..2b3aa7ee3d0585817b5afe1a30a231f57841785d
--- /dev/null
+++ b/core/migrations/0007_auto_20180105_1136.py
@@ -0,0 +1,41 @@
+# Generated by Django 2.0 on 2018-01-05 11:36
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0006_auto_20180104_2001'),
+    ]
+
+    operations = [
+        migrations.RenameModel(
+            old_name='Student',
+            new_name='StudentInfo',
+        ),
+        migrations.RemoveField(
+            model_name='reviewer',
+            name='user',
+        ),
+        migrations.RemoveField(
+            model_name='tutor',
+            name='user',
+        ),
+        migrations.RemoveField(
+            model_name='feedback',
+            name='of_reviewer',
+        ),
+        migrations.AddField(
+            model_name='useraccount',
+            name='role',
+            field=models.CharField(choices=[('Student', 'student'), ('Tutor', 'tutor'), ('Reviewer', 'reviewer')], default='Student', max_length=50),
+            preserve_default=False,
+        ),
+        migrations.DeleteModel(
+            name='Reviewer',
+        ),
+        migrations.DeleteModel(
+            name='Tutor',
+        ),
+    ]
diff --git a/core/models.py b/core/models.py
index 1f7d19274a567ae7864806d15039ce25c11e8054..40af977566befbd7000bd739e75b92595482b974 100644
--- a/core/models.py
+++ b/core/models.py
@@ -149,43 +149,48 @@ class UserAccount(AbstractUser):
     fullname = models.CharField('full name', max_length=70, blank=True)
     is_admin = models.BooleanField(default=False)
 
-    def get_associated_user(self) -> models.Model:
-        """ Returns the user type that is associated with this user obj """
-        return \
-            (hasattr(self, 'student') and self.student) or \
-            (hasattr(self, 'reviewer') and self.reviewer) or \
-            (hasattr(self, 'tutor') and self.tutor)
+    STUDENT = 'Student'
+    TUTOR = 'Tutor'
+    REVIEWER = 'Reviewer'
+
+    ROLE_CHOICES = (
+        (STUDENT, 'student'),
+        (TUTOR, 'tutor'),
+        (REVIEWER, 'reviewer')
+    )
 
-    def is_reviewer(self):
-        return hasattr(self, 'reviewer')
-
-    def is_tutor(self):
-        return hasattr(self, 'tutor')
+    role = models.CharField(max_length=50, choices=ROLE_CHOICES)
 
     def is_student(self):
-        return hasattr(self, 'student')
+        return self.role == 'Student'
 
+    def is_tutor(self):
+        return self.role == 'Tutor'
 
-class Tutor(models.Model):
-    user = models.OneToOneField(get_user_model(),
-                                on_delete=models.CASCADE,
-                                related_name='tutor')
+    def is_reviewer(self):
+        return self.role == 'Reviewer'
 
     def get_feedback_count(self) -> int:
         return self.feedback_list.count()
 
+    @classmethod
+    def get_students(cls):
+        return cls.objects.filter(role=cls.STUDENT)
 
-class Reviewer(models.Model):
-    user = models.OneToOneField(get_user_model(),
-                                on_delete=models.CASCADE,
-                                related_name='reviewer')
+    @classmethod
+    def get_tutors(cls):
+        return cls.objects.filter(role=cls.TUTOR)
 
+    @classmethod
+    def get_reviewers(cls):
+        return cls.objects.filter(role=cls.REVIEWER)
 
-class Student(models.Model):
+
+class StudentInfo(models.Model):
     """
-    The student model includes all information of a student, that we got from
-    the E-Learning output, along with some useful classmethods that provide
-    specially annotated QuerySets.
+    The StudentInfo model includes all information of a student, that we got
+    from the E-Learning output, along with some useful classmethods that
+    provide specially annotated QuerySets.
 
     Information like email (if given), and the username are stored in the
     associated user model.
@@ -331,7 +336,7 @@ class Submission(models.Model):
         on_delete=models.PROTECT,
         related_name='submissions')
     student = models.ForeignKey(
-        Student,
+        StudentInfo,
         on_delete=models.CASCADE,
         related_name='submissions')
 
@@ -389,12 +394,6 @@ class Feedback(models.Model):
         related_name='feedback_list',
         blank=True,
         null=True)
-    of_reviewer = models.ForeignKey(
-        Reviewer,
-        on_delete=models.SET_NULL,
-        related_name='reviewed_submissions',
-        blank=True,
-        null=True)
 
     # how was this feedback created
     (
@@ -527,6 +526,15 @@ class GeneralTaskSubscription(models.Model):
             subscription=self,
             submission=task)[0]
 
+    def reserve_all_assignments_for_a_student(self):
+        assert self.query_type == self.STUDENT_QUERY
+
+        try:
+            while True:
+                self.get_or_create_work_assignment()
+        except SubscriptionEnded as err:
+            log.info(f'Loaded all subscriptions of student {self.query_key}')
+
     def _create_new_assignment_if_subscription_empty(self):
         if self.assignments.filter(is_done=False).count() < 1:
             self.get_or_create_work_assignment()
diff --git a/core/permissions.py b/core/permissions.py
index 5466fa2357f5f31a85085a172090934985e70f03..7c5f5cf0e427c0acf8cd12a08b3b6864f8c9e51b 100644
--- a/core/permissions.py
+++ b/core/permissions.py
@@ -4,26 +4,23 @@ from django.http import HttpRequest
 from django.views import View
 from rest_framework import permissions
 
-from core.models import Reviewer, Student, Tutor
-
 log = logging.getLogger(__name__)
 
 
-class IsUserGenericPermission(permissions.BasePermission):
+class IsUserRoleGenericPermission(permissions.BasePermission):
     """ Generic class that encapsulates how to identify someone
     as a member of a user Group """
 
     def has_permission(self, request: HttpRequest, view: View) -> bool:
         """ required by BasePermission. Check if user is instance of any
         of the models provided in class' models attribute """
-        assert self.models is not None, (
-            "'%s' has to include a `models` attribute"
+        assert self.roles is not None, (
+            "'%s' has to include a `roles` attribute"
             % self.__class__.__name__
         )
 
         user = request.user
-        is_authorized = user.is_authenticated and any(isinstance(
-            user.get_associated_user(), models) for models in self.models)
+        is_authorized = user.is_authenticated and user.role in self.roles
 
         if not is_authorized:
             log.warn('User "%s" has no permission to view %s',
@@ -32,21 +29,21 @@ class IsUserGenericPermission(permissions.BasePermission):
         return is_authorized
 
 
-class IsStudent(IsUserGenericPermission):
+class IsStudent(IsUserRoleGenericPermission):
     """ Has student permissions """
-    models = (Student,)
+    roles = ('Student', )
 
 
-class IsReviewer(IsUserGenericPermission):
+class IsReviewer(IsUserRoleGenericPermission):
     """ Has reviewer permissions """
-    models = (Reviewer,)
+    roles = ('Reviewer', )
 
 
-class IsTutor(IsUserGenericPermission):
+class IsTutor(IsUserRoleGenericPermission):
     """ Has tutor permissions """
-    models = (Tutor,)
+    roles = ('Tutor', )
 
 
-class IsTutorOrReviewer(IsUserGenericPermission):
+class IsTutorOrReviewer(IsUserRoleGenericPermission):
     """ Has tutor or reviewer permissions """
-    models = (Tutor, Reviewer,)
+    roles = ('Tutor', 'Reviewer')
diff --git a/core/serializers.py b/core/serializers.py
index c767054f769b1857cdcf08d901e2be772e1dd054..83c8bf1c60b48460630b7ddb918973c2bbc2ccf1 100644
--- a/core/serializers.py
+++ b/core/serializers.py
@@ -1,13 +1,12 @@
 import logging
 
-from django.db import IntegrityError
 from django.core.exceptions import ObjectDoesNotExist
-
 from drf_dynamic_fields import DynamicFieldsMixin
 from rest_framework import serializers
 
-from core.models import (ExamType, Feedback, GeneralTaskSubscription, Student,
-                         Submission, SubmissionType, Tutor,
+from core import models
+from core.models import (ExamType, Feedback, GeneralTaskSubscription,
+                         StudentInfo, Submission, SubmissionType,
                          TutorSubmissionAssignment)
 from util.factories import GradyUserFactory
 
@@ -17,19 +16,7 @@ user_factory = GradyUserFactory()
 
 class DynamicFieldsModelSerializer(DynamicFieldsMixin,
                                    serializers.ModelSerializer):
-    def __init__(self, *args, **kwargs):
-        # Don't pass the 'fields' arg up to the superclass
-        fields = kwargs.pop('fields', None)
-
-        # Instantiate the superclass normally
-        super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
-
-        if fields is not None:
-            # Drop any fields that are not specified in the `fields` argument.
-            allowed = set(fields)
-            existing = set(self.fields.keys())
-            for field_name in existing - allowed:
-                self.fields.pop(field_name)
+    pass
 
 
 class ExamSerializer(DynamicFieldsModelSerializer):
@@ -47,16 +34,12 @@ class FeedbackSerializer(DynamicFieldsModelSerializer):
         log.debug(data)
         assignment_id = data.pop('assignment_id')
         score = data.get('score')
-        creator = self.context.get('request').user
 
         try:
             assignment = TutorSubmissionAssignment.objects.get(
                 assignment_id=assignment_id)
         except ObjectDoesNotExist as err:
-            raise serializers.ValidationError('No assignment for id')
-
-        if not assignment.subscription.owner == creator:
-            raise serializers.ValidationError('This is not your assignment')
+            raise serializers.ValidationError('No assignment for given id.')
 
         submission = assignment.submission
         if not 0 <= score <= submission.type.full_score:
@@ -70,12 +53,10 @@ class FeedbackSerializer(DynamicFieldsModelSerializer):
         return {
             **data,
             'assignment': assignment,
-            'of_tutor': creator,
             'of_submission': submission
         }
 
     def create(self, validated_data) -> Feedback:
-        log.debug(validated_data)
         assignment = validated_data.pop('assignment')
         assignment.set_done()
 
@@ -86,92 +67,71 @@ class FeedbackSerializer(DynamicFieldsModelSerializer):
         fields = ('assignment_id', 'text', 'score')
 
 
-class TestSerializer(DynamicFieldsModelSerializer):
-
-    class Meta:
-        model = Test
-        fields = ('name', 'label', 'annotation')
-
-
-class SubmissionTypeListSerializer(DynamicFieldsModelSerializer):
-    fullScore = serializers.IntegerField(source='full_score')
+class SubmissionTypeSerializer(DynamicFieldsModelSerializer):
+    fullScore = serializers.ReadOnlyField(source='full_score')
+    typeId = serializers.ReadOnlyField(source='id')
 
     class Meta:
         model = SubmissionType
-        fields = ('id', 'name', 'fullScore')
-
-
-class SubmissionTypeSerializer(SubmissionTypeListSerializer):
-
-    class Meta:
-        model = SubmissionType
-        fields = ('id', 'name', 'fullScore', 'description', 'solution')
+        fields = ('typeId', 'name', 'fullScore', 'description', 'solution')
 
 
 class SubmissionSerializer(DynamicFieldsModelSerializer):
-    type = SubmissionTypeSerializer()
-    feedback = FeedbackSerializer()
-    tests = TestSerializer(many=True)
-
-    class Meta:
-        model = Submission
-        fields = ('type', 'text', 'feedback', 'tests')
-
-
-class SubmissionListSerializer(DynamicFieldsModelSerializer):
-    type = SubmissionTypeListSerializer()
-    # TODO change this according to new feedback model
-    feedback = FeedbackSerializer(fields=('score',))
+    feedback = serializers.ReadOnlyField(source='feedback.text')
+    score = serializers.ReadOnlyField(source='feedback.score')
+    typeId = serializers.ReadOnlyField(source='type.id')
+    typeName = serializers.ReadOnlyField(source='type.name')
+    fullScore = serializers.ReadOnlyField(source='type.full_score')
 
     class Meta:
         model = Submission
-        fields = ('type', 'feedback')
+        fields = ('typeId', 'typeName', 'text',
+                  'feedback', 'score', 'fullScore')
 
 
-class StudentSerializer(DynamicFieldsModelSerializer):
+class StudentInfoSerializer(DynamicFieldsModelSerializer):
     name = serializers.ReadOnlyField(source='user.fullname')
-    matrikel_no = serializers.ReadOnlyField(source='user.matrikel_no')
+    user = serializers.ReadOnlyField(source='user.username')
     exam = ExamSerializer()
-    submissions = SubmissionListSerializer(many=True)
+    submissions = SubmissionSerializer(many=True)
 
     class Meta:
-        model = Student
-        fields = ('name', 'user', 'matrikel_no', 'exam', 'submissions')
+        model = StudentInfo
+        fields = ('name', 'user', 'exam', 'submissions')
 
 
 class SubmissionNoTextFieldsSerializer(DynamicFieldsModelSerializer):
     score = serializers.ReadOnlyField(source='feedback.score')
-    type = serializers.ReadOnlyField(source='type.name')
-    full_score = serializers.ReadOnlyField(source='type.full_score')
+    typeId = serializers.ReadOnlyField(source='type.id')
+    fullScore = serializers.ReadOnlyField(source='type.full_score')
 
     class Meta:
         model = Submission
-        fields = ('type', 'score', 'full_score')
+        fields = ('typeId', 'score', 'fullScore')
 
 
-class StudentSerializerForListView(DynamicFieldsModelSerializer):
+class StudentInfoSerializerForListView(DynamicFieldsModelSerializer):
     name = serializers.ReadOnlyField(source='user.fullname')
     user = serializers.ReadOnlyField(source='user.username')
     exam = serializers.ReadOnlyField(source='exam.module_reference')
     submissions = SubmissionNoTextFieldsSerializer(many=True)
 
     class Meta:
-        model = Student
+        model = StudentInfo
         fields = ('name', 'user', 'exam', 'submissions')
 
 
 class TutorSerializer(DynamicFieldsModelSerializer):
-    username = serializers.CharField(source='user.username')
     feedback_count = serializers.IntegerField(source='get_feedback_count',
                                               read_only=True)
 
-    def create(self, validated_data) -> Tutor:
+    def create(self, validated_data) -> models.UserAccount:
         log.info("Crating tutor from data %s", validated_data)
         return user_factory.make_tutor(
-            username=validated_data['user']['username'])
+            username=validated_data['username'])
 
     class Meta:
-        model = Tutor
+        model = models.UserAccount
         fields = ('username', 'feedback_count')
 
 
@@ -186,12 +146,12 @@ class AssignmentSerializer(DynamicFieldsModelSerializer):
 
 class SubmissionAssignmentSerializer(DynamicFieldsModelSerializer):
     text = serializers.ReadOnlyField()
-    type_id = serializers.ReadOnlyField(source='type.id')
-    full_score = serializers.ReadOnlyField(source='type.full_score')
+    typeId = serializers.ReadOnlyField(source='type.id')
+    fullScore = serializers.ReadOnlyField(source='type.full_score')
 
     class Meta:
         model = Submission
-        fields = ('submission_id', 'type_id', 'text', 'full_score')
+        fields = ('submission_id', 'typeId', 'text', 'fullScore')
 
 
 class AssignmentDetailSerializer(DynamicFieldsModelSerializer):
@@ -209,27 +169,29 @@ class SubscriptionSerializer(DynamicFieldsModelSerializer):
     assignments = AssignmentSerializer(read_only=True, many=True)
 
     def validate(self, data):
+        data['owner'] = self.context['request'].user
+
         if 'query_key' in data != \
                 data['query_type'] == GeneralTaskSubscription.RANDOM:
             raise serializers.ValidationError(
                 f'The {data["query_type"]} query_type does not work with the'
                 f'provided key')
 
-        return data
-
-    def create(self, validated_data) -> GeneralTaskSubscription:
-        subscription = GeneralTaskSubscription.objects.create(
-            owner=self.context.get("request").user,
-            **validated_data)
         try:
-            subscription._create_new_assignment_if_subscription_empty()
-        except IntegrityError as err:
-            log.debug(err)
-            raise
+            GeneralTaskSubscription.objects.get(
+                owner=data['owner'],
+                query_type=data['query_type'],
+                query_key=data.get('query_key', None))
+        except ObjectDoesNotExist:
+            pass
+        else:
             raise serializers.ValidationError(
-                "Oh great, you raised an IntegrityError. I'm disappointed.")
+                'The user already has the subscription')
 
-        return subscription
+        return data
+
+    def create(self, validated_data) -> GeneralTaskSubscription:
+        return GeneralTaskSubscription.objects.create(**validated_data)
 
     class Meta:
         model = GeneralTaskSubscription
diff --git a/core/tests/test_access_rights.py b/core/tests/test_access_rights.py
index 9c22248b907e106d7a78448fb011655a04b5bb45..8a636dbf0191a29d4f98828310859b4d3508a49c 100644
--- a/core/tests/test_access_rights.py
+++ b/core/tests/test_access_rights.py
@@ -29,17 +29,17 @@ class AccessRightsOfStudentAPIViewTests(APITestCase):
         self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
 
     def test_tutor_has_no_access(self):
-        force_authenticate(self.request, user=self.tutor.user)
+        force_authenticate(self.request, user=self.tutor)
         response = self.view(self.request)
         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
 
     def test_reviewer_has_no_access(self):
-        force_authenticate(self.request, user=self.reviewer.user)
+        force_authenticate(self.request, user=self.reviewer)
         response = self.view(self.request)
         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
 
     def test_student_is_authorized(self):
-        force_authenticate(self.request, user=self.student.user)
+        force_authenticate(self.request, user=self.student)
         response = self.view(self.request)
         self.assertEqual(response.status_code, status.HTTP_200_OK)
 
@@ -64,17 +64,17 @@ class AccessRightsOfTutorAPIViewTests(APITestCase):
         self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
 
     def test_student_has_no_access(self):
-        force_authenticate(self.request, user=self.student.user)
+        force_authenticate(self.request, user=self.student)
         response = self.view(self.request)
         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
 
     def test_tutor_has_no_access(self):
-        force_authenticate(self.request, user=self.tutor.user)
+        force_authenticate(self.request, user=self.tutor)
         response = self.view(self.request)
         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
 
     def test_reviewer_has_access(self):
-        force_authenticate(self.request, user=self.reviewer.user)
+        force_authenticate(self.request, user=self.reviewer)
         response = self.view(self.request)
         self.assertEqual(response.status_code, status.HTTP_200_OK)
 
@@ -100,17 +100,17 @@ class AccessRightsOfStudentReviewerAPIViewTest(APITestCase):
         self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
 
     def test_student_has_no_access(self):
-        force_authenticate(self.request, user=self.student.user)
+        force_authenticate(self.request, user=self.student)
         response = self.view(self.request)
         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
 
     def test_tutor_has_no_access(self):
-        force_authenticate(self.request, user=self.tutor.user)
+        force_authenticate(self.request, user=self.tutor)
         response = self.view(self.request)
         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
 
     def test_reviewer_has_access(self):
-        force_authenticate(self.request, user=self.reviewer.user)
+        force_authenticate(self.request, user=self.reviewer)
         response = self.view(self.request)
         self.assertEqual(response.status_code, status.HTTP_200_OK)
 
@@ -134,16 +134,16 @@ class AccessRightsOfExamTypeAPIViewTest(APITestCase):
         self.view = ExamApiViewSet.as_view({'get': 'list'})
 
     def test_student_has_no_access(self):
-        force_authenticate(self.request, user=self.student.user)
+        force_authenticate(self.request, user=self.student)
         response = self.view(self.request)
         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
 
     def test_tutor_has_no_access(self):
-        force_authenticate(self.request, user=self.tutor.user)
+        force_authenticate(self.request, user=self.tutor)
         response = self.view(self.request)
         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
 
     def test_reviewer_has_access(self):
-        force_authenticate(self.request, user=self.reviewer.user)
+        force_authenticate(self.request, user=self.reviewer)
         response = self.view(self.request)
         self.assertEqual(response.status_code, status.HTTP_200_OK)
diff --git a/core/tests/test_commands.py b/core/tests/test_commands.py
index 8286c8e10f1d318750fbad6a242662821105ce51..e85b8ee5f58b090755ec24e3ebd0b27801f7ed6b 100644
--- a/core/tests/test_commands.py
+++ b/core/tests/test_commands.py
@@ -1,13 +1,12 @@
-from django.core.management import call_command
+import json
+import tempfile
+
 from django.contrib.auth import get_user_model
+from django.core.management import call_command
 from django.test import TestCase
 
 from util.factories import GradyUserFactory
 
-from core.models import Student
-import tempfile
-import json
-
 
 class CommandsTestCase(TestCase):
 
@@ -31,5 +30,5 @@ class CommandsTestCase(TestCase):
             args = [matno2username.name]
             call_command('replaceusernames', *args, **{})
 
-        student = Student.objects.get(matrikel_no=88884444)
-        self.assertEqual('after', student.user.username)
+        student = get_user_model().objects.get(student__matrikel_no=88884444)
+        self.assertEqual('after', student.username)
diff --git a/core/tests/test_examlist.py b/core/tests/test_examlist.py
index f1e99b2d0182624576c3079149564a481317b185..b5be17ccb0f15831591d7f0619fc13973648b2d6 100644
--- a/core/tests/test_examlist.py
+++ b/core/tests/test_examlist.py
@@ -24,7 +24,7 @@ class ExamListTest(APITestCase):
                                                 total_score=90,
                                                 pass_score=45)
         force_authenticate(self.request,
-                           self.user_factory.make_reviewer().user)
+                           self.user_factory.make_reviewer())
         self.view = ExamApiViewSet.as_view({'get': 'list'})
         self.response = self.view(self.request)
 
diff --git a/core/tests/test_factory_and_feedback.py b/core/tests/test_factory_and_feedback.py
index 42ed4a4e2e1434cc1e4d63898c50a9ae75b153fd..5b5828901c33fcf9f2393e48c76bef4f1a884686 100644
--- a/core/tests/test_factory_and_feedback.py
+++ b/core/tests/test_factory_and_feedback.py
@@ -1,6 +1,7 @@
 from django.test import TestCase
 
-from core.models import Reviewer, Student, Tutor
+from core import models
+from core.models import StudentInfo
 from util.factories import GradyUserFactory
 
 
@@ -10,39 +11,28 @@ class FactoryTestCase(TestCase):
 
     def test_make_student(self):
 
-        student = self.factory.make_student()
+        user = self.factory.make_student()
 
-        self.assertEqual(Student.objects.count(), 1)
-        self.assertEqual(student.exam, None)
-        self.assertEqual(len(str(student.matrikel_no)), 8)
+        self.assertEqual(StudentInfo.objects.count(), 1)
+        self.assertEqual(user.student.exam, None)
+        self.assertEqual(len(str(user.student.matrikel_no)), 8)
 
     def test_can_create_reviewer(self):
-        self.assertTrue(isinstance(self.factory.make_reviewer(), Reviewer))
+        self.assertTrue(isinstance(self.factory.make_reviewer(),
+                                   models.UserAccount))
 
     def test_reviewer_appears_in_query_set(self):
-        self.assertIn(self.factory.make_reviewer(), Reviewer.objects.all())
+        self.assertIn(self.factory.make_reviewer(),
+                      models.UserAccount.objects.all())
 
     def test_can_create_tutor(self):
-        self.assertIn(self.factory.make_tutor(), Tutor.objects.all())
+        self.assertIn(self.factory.make_tutor(),
+                      models.UserAccount.objects.all())
 
-    def test_can_create_student(self):
-        self.assertIn(self.factory.make_student(), Student.objects.all())
+    def test_can_create_student_user(self):
+        self.assertIn(self.factory.make_student(),
+                      models.UserAccount.objects.all())
 
-
-class AccountsTestCase(TestCase):
-
-    factory = GradyUserFactory()
-
-    def _test_user_obj_returns_correct_type(self, maker):
-        model = maker()
-        user = model.user
-        self.assertEqual(user.get_associated_user(), model)
-
-    def test_user_obj_returns_correct_type_student(self):
-        self._test_user_obj_returns_correct_type(self.factory.make_student)
-
-    def test_user_obj_returns_correct_type_tutor(self):
-        self._test_user_obj_returns_correct_type(self.factory.make_tutor)
-
-    def test_user_obj_returns_correct_type_reviewer(self):
-        self._test_user_obj_returns_correct_type(self.factory.make_reviewer)
+    def test_can_create_student_info(self):
+        self.assertIn(self.factory.make_student().student,
+                      StudentInfo.objects.all())
diff --git a/core/tests/test_functional_views.py b/core/tests/test_functional_views.py
index 5a99c8b914092f3bc786f8d710b7d9ccec7852ac..7763b5c63f990a6e727c34f512f6014c6ce400e6 100644
--- a/core/tests/test_functional_views.py
+++ b/core/tests/test_functional_views.py
@@ -19,16 +19,16 @@ class GetUserRoleTest(APITestCase):
         self.request = self.factory.get(reverse('user-role'))
 
     def test_get_user_model_returns_student(self):
-        force_authenticate(self.request, user=self.student.user)
+        force_authenticate(self.request, user=self.student)
         response = get_user_role(self.request)
         self.assertEqual(response.data['role'], 'Student')
 
     def test_get_user_model_returns_tutor(self):
-        force_authenticate(self.request, user=self.tutor.user)
+        force_authenticate(self.request, user=self.tutor)
         response = get_user_role(self.request)
         self.assertEqual(response.data['role'], 'Tutor')
 
     def test_get_user_model_returns_reviewer(self):
-        force_authenticate(self.request, user=self.reviewer.user)
+        force_authenticate(self.request, user=self.reviewer)
         response = get_user_role(self.request)
         self.assertEqual(response.data['role'], 'Reviewer')
diff --git a/core/tests/test_student_page.py b/core/tests/test_student_page.py
index 2af05f2be40a884bb09934e1155265f3f2660a82..169e794120498b0b3365bd9e55224a5c75bbc2f0 100644
--- a/core/tests/test_student_page.py
+++ b/core/tests/test_student_page.py
@@ -49,7 +49,7 @@ class StudentPageTests(APITestCase):
             }]
         })
 
-        self.student = self.test_data['students'][0]
+        self.student = self.test_data['students'][0].student
         self.tutor = self.test_data['tutors'][0]
         self.reviewer = self.test_data['reviewers'][0]
         self.submission = self.test_data['submissions'][0]
@@ -93,22 +93,22 @@ class StudentPageTests(APITestCase):
     # Tests concerning submission data
     def test_a_student_submissions_contains_type_name(self):
         self.assertEqual(
-            self.submission_list_first_entry['type']['name'],
+            self.submission_list_first_entry['typeName'],
             self.student.submissions.first().type.name)
 
     def test_a_student_submissions_contains_type_id(self):
         self.assertEqual(
-            self.submission_list_first_entry['type']['id'],
+            self.submission_list_first_entry['typeId'],
             self.student.submissions.first().type.id)
 
     def test_submission_data_contains_full_score(self):
         self.assertEqual(
-            self.submission_list_first_entry['type']['fullScore'],
+            self.submission_list_first_entry['fullScore'],
             self.student.submissions.first().type.full_score)
 
     def test_submission_data_contains_feedback_score(self):
         self.assertEqual(
-            self.submission_list_first_entry['feedback']['score'],
+            self.submission_list_first_entry['score'],
             self.student.submissions.first().feedback.score)
 
     # We don't want a matriculation number here
@@ -148,7 +148,7 @@ class StudentSelfSubmissionsTests(APITestCase):
             }]
         })
 
-        self.student = self.test_data['students'][0]
+        self.student = self.test_data['students'][0].student
         self.tutor = self.test_data['tutors'][0]
         self.submission = self.test_data['submissions'][0]
         self.feedback = self.submission.feedback
@@ -165,29 +165,19 @@ class StudentSelfSubmissionsTests(APITestCase):
     # Tests concerning submission data
     def test_a_student_submissions_contains_type_name(self):
         self.assertEqual(
-            self.submission_list_first_entry['type']['name'],
+            self.submission_list_first_entry['typeName'],
             self.student.submissions.first().type.name)
 
     def test_a_student_submissions_contains_type_id(self):
         self.assertEqual(
-            self.submission_list_first_entry['type']['id'],
+            self.submission_list_first_entry['typeId'],
             self.student.submissions.first().type.id)
 
     def test_submission_data_contains_full_score(self):
         self.assertEqual(
-            self.submission_list_first_entry['type']['fullScore'],
+            self.submission_list_first_entry['fullScore'],
             self.student.submissions.first().type.full_score)
 
-    def test_submission_data_contains_description(self):
-        self.assertEqual(
-            self.submission_list_first_entry['type']['description'],
-            self.student.submissions.first().type.description)
-
-    def test_submission_data_contains_solution(self):
-        self.assertEqual(
-            self.submission_list_first_entry['type']['solution'],
-            self.student.submissions.first().type.solution)
-
     def test_submission_data_contains_text(self):
         self.assertEqual(
             self.submission_list_first_entry['text'],
@@ -195,12 +185,12 @@ class StudentSelfSubmissionsTests(APITestCase):
 
     def test_submission_data_contains_feedback_score(self):
         self.assertEqual(
-            self.submission_list_first_entry['feedback']['score'],
+            self.submission_list_first_entry['score'],
             self.student.submissions.first().feedback.score)
 
     def test_submission_data_contains_feedback_text(self):
         self.assertEqual(
-            self.submission_list_first_entry['feedback']['text'],
+            self.submission_list_first_entry['feedback'],
             self.student.submissions.first().feedback.text)
 
     # We don't want a matriculation number here
diff --git a/core/tests/test_student_reviewer_viewset.py b/core/tests/test_student_reviewer_viewset.py
index d486641ce9bcb6a9e936988ca2b4a5bd5d10e4fd..ab39d9840366d06db063d36e6d486e72e622e203 100644
--- a/core/tests/test_student_reviewer_viewset.py
+++ b/core/tests/test_student_reviewer_viewset.py
@@ -48,14 +48,14 @@ class StudentPageTests(APITestCase):
             }]
         })
 
-        self.student = self.test_data['students'][0]
+        self.student = self.test_data['students'][0].student
         self.reviewer = self.test_data['reviewers'][0]
         self.submission = self.test_data['submissions'][0]
 
         self.request = self.factory.get(reverse('student-list'))
         self.view = StudentReviewerApiViewSet.as_view({'get': 'list'})
 
-        force_authenticate(self.request, user=self.reviewer.user)
+        force_authenticate(self.request, user=self.reviewer)
         self.response = self.view(self.request)
 
     def test_can_access(self):
@@ -70,4 +70,4 @@ class StudentPageTests(APITestCase):
 
     def test_submissions_full_score_is_included(self):
         self.assertEqual(self.student.submissions.first().type.full_score,
-                         self.response.data[0]['submissions'][0]['full_score'])
+                         self.response.data[0]['submissions'][0]['fullScore'])
diff --git a/core/tests/test_submissiontypeview.py b/core/tests/test_submissiontypeview.py
index 0bd370da38fb30272d295784593848663ac09314..5f35ffd7c2d8ceff99dc4d8f23c8a64a8d4e3e8b 100644
--- a/core/tests/test_submissiontypeview.py
+++ b/core/tests/test_submissiontypeview.py
@@ -23,7 +23,7 @@ class SubmissionTypeViewTestList(APITestCase):
                                       full_score=20,
                                       description='Whatever')
         force_authenticate(self.request,
-                           self.user_factory.make_reviewer().user)
+                           self.user_factory.make_reviewer())
         self.view = SubmissionTypeApiView.as_view({'get': 'list'})
         self.response = self.view(self.request)
 
@@ -54,7 +54,7 @@ class SubmissionTypeViewTestRetrieve(APITestCase):
                                       description='Whatever')
         self.pk = SubmissionType.objects.first().pk
         force_authenticate(self.request,
-                           self.user_factory.make_reviewer().user)
+                           self.user_factory.make_reviewer())
         self.view = SubmissionTypeApiView.as_view({'get': 'retrieve'})
         self.response = self.view(self.request, pk=self.pk)
 
@@ -62,7 +62,7 @@ class SubmissionTypeViewTestRetrieve(APITestCase):
         self.assertEqual(self.response.status_code, status.HTTP_200_OK)
 
     def test_get_id(self):
-        self.assertEqual(self.pk, self.response.data['id'])
+        self.assertEqual(self.pk, self.response.data['typeId'])
 
     def test_get_sumbission_type_name(self):
         self.assertEqual('Hard question', self.response.data['name'])
diff --git a/core/tests/test_subscription_assignment_service.py b/core/tests/test_subscription_assignment_service.py
index d96aae0e56aa4edc50ee9d0e6a57b129edcb2fa9..ebdcd33d0de612dc6dd05950c46379e8595da0d7 100644
--- a/core/tests/test_subscription_assignment_service.py
+++ b/core/tests/test_subscription_assignment_service.py
@@ -1,12 +1,12 @@
-
-from django.test import TestCase
+from rest_framework import status
+from rest_framework.test import APIClient, APITestCase
 
 from core.models import (GeneralTaskSubscription, Submission, SubmissionType,
                          SubscriptionEnded)
-from util.factories import GradyUserFactory
+from util.factories import GradyUserFactory, make_test_data
 
 
-class GeneralTaskSubscriptionRandomTest(TestCase):
+class GeneralTaskSubscriptionRandomTest(APITestCase):
 
     @classmethod
     def setUpTestData(cls):
@@ -20,12 +20,14 @@ class GeneralTaskSubscriptionRandomTest(TestCase):
         self.submission_type = SubmissionType.objects.create(
             name='submission_01', full_score=14)
         self.submission_01 = Submission.objects.create(
-            type=self.submission_type, student=self.s1, text='I really failed')
+            type=self.submission_type, student=self.s1.student,
+            text='I really failed')
         self.submission_02 = Submission.objects.create(
-            type=self.submission_type, student=self.s2, text='I like apples')
+            type=self.submission_type, student=self.s2.student,
+            text='I like apples')
 
         self.subscription = GeneralTaskSubscription.objects.create(
-            owner=self.t.user, query_type=GeneralTaskSubscription.RANDOM)
+            owner=self.t, query_type=GeneralTaskSubscription.RANDOM)
 
     def test_subscription_gets_an_assignment(self):
         self.subscription._create_new_assignment_if_subscription_empty()
@@ -54,3 +56,120 @@ class GeneralTaskSubscriptionRandomTest(TestCase):
         assignment = self.subscription.get_oldest_unfinished_assignment()
         self.assertEqual(assignment,
                          self.subscription.get_oldest_unfinished_assignment())
+
+
+class TestApiEndpoints(APITestCase):
+
+    @classmethod
+    def setUpTestData(cls):
+        cls.data = make_test_data(data_dict={
+            'submission_types': [
+                {
+                    'name': '01. Sort this or that',
+                    'full_score': 35,
+                    'description': 'Very complicated',
+                    'solution': 'Trivial!'
+                },
+                {
+                    'name': '02. Merge this or that or maybe even this',
+                    'full_score': 35,
+                    'description': 'Very complicated',
+                    'solution': 'Trivial!'
+                },
+                {
+                    'name': '03. This one exists for the sole purpose to test',
+                    'full_score': 30,
+                    'description': 'Very complicated',
+                    'solution': 'Trivial!'
+                }
+            ],
+            'students': [
+                {'username': 'student01'},
+                {'username': 'student02'}
+            ],
+            'tutors': [
+                {'username': 'tutor01'},
+                {'username': 'tutor02'}
+            ],
+            'submissions': [
+                {
+                    'text': 'function blabl\n'
+                    '   on multi lines\n'
+                    '       for blabla in bla:\n'
+                    '           lorem ipsum und so\n',
+                    'type': '01. Sort this or that',
+                    'user': 'student01',
+                    'feedback': {
+                            'text': 'Not good!',
+                            'score': 5,
+                            'of_tutor': 'tutor01',
+                            'is_final': True
+                    }
+                },
+                {
+                    'text': 'function blabl\n'
+                    '       asasxasx\n'
+                    '           lorem ipsum und so\n',
+                    'type': '02. Merge this or that or maybe even this',
+                    'user': 'student01'
+                },
+                {
+                    'text': 'function blabl\n'
+                    '   on multi lines\n'
+                    '       asasxasx\n'
+                    '           lorem ipsum und so\n',
+                    'type': '03. This one exists for the sole purpose to test',
+                    'user': 'student01'
+                },
+                {
+                    'text': 'function lorem ipsum etc\n',
+                    'type': '03. This one exists for the sole purpose to test',
+                    'user': 'student02'
+                },
+            ]}
+        )
+
+    def test_can_create_a_subscription(self):
+        client = APIClient()
+        client.force_authenticate(user=self.data['tutors'][0])
+
+        response = client.post('/api/subscription/', {'query_type': 'random'})
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+
+    def test_create_subscription_and_get_one_assignment(self):
+        client = APIClient()
+        client.force_authenticate(user=self.data['tutors'][0])
+
+        response = client.post('/api/subscription/', {'query_type': 'random'})
+
+        self.assertEqual('tutor01', response.data['owner'])
+
+    def test_subscription_has_next_assignment(self):
+        client = APIClient()
+        client.force_authenticate(user=self.data['tutors'][0])
+
+        response_subs = client.post(
+            '/api/subscription/', {'query_type': 'random'})
+        subscription_id = response_subs.data['subscription_id']
+        assignment_id = response_subs.data['assignments'][0]['assignment_id']
+
+        response_current = client.get(
+            f'/api/subscription/{subscription_id}/assignments/current/')
+
+        self.assertEqual(1, len(response_subs.data['assignments']))
+        self.assertEqual(assignment_id,
+                         response_current.data['assignment_id'])
+
+    def test_subscription_can_assign_to_student(self):
+        client = APIClient()
+        client.force_authenticate(user=self.data['tutors'][0])
+
+        response_subs = client.post(
+            '/api/subscription/', {
+                'query_type': 'student',
+                'query_key': 'student01'
+            })
+
+        assignments = response_subs.data['assignments']
+        self.assertEqual(2, len(assignments))
diff --git a/core/tests/test_tutor_api_endpoints.py b/core/tests/test_tutor_api_endpoints.py
index 8c8f59a5929bf73550d7db9bab80ba52168e6e1d..133b23826e0219086fe911a42f26a7fd4191017f 100644
--- a/core/tests/test_tutor_api_endpoints.py
+++ b/core/tests/test_tutor_api_endpoints.py
@@ -10,7 +10,7 @@ from rest_framework.reverse import reverse
 from rest_framework.test import (APIClient, APIRequestFactory, APITestCase,
                                  force_authenticate)
 
-from core.models import Feedback, Tutor
+from core.models import Feedback
 from core.views import TutorApiViewSet
 from util.factories import GradyUserFactory
 
@@ -31,16 +31,16 @@ class TutorDeleteTest(APITestCase):
                                                    args=['UFO']))
         self.view = TutorApiViewSet.as_view({'delete': 'destroy'})
 
-        force_authenticate(self.request, user=self.reviewer.user)
+        force_authenticate(self.request, user=self.reviewer)
         self.response = self.view(self.request, username='UFO')
 
     def test_can_delete_tutor_soapbox(self):
         """ see if the tutor was deleted """
-        self.assertEqual(0, Tutor.objects.count())
+        self.assertEqual(0, get_user_model().get_tutors().count())
 
     def test_user_is_deleted_too(self):
         """ see if the associated user was deleted (reviewer remains) """
-        self.assertEqual(1, get_user_model().objects.count())
+        self.assertNotIn(self.tutor, get_user_model().objects.all())
 
 
 class TutorListTests(APITestCase):
@@ -57,7 +57,7 @@ class TutorListTests(APITestCase):
         self.request = self.factory.get(reverse('tutor-list'))
         self.view = TutorApiViewSet.as_view({'get': 'list'})
 
-        force_authenticate(self.request, user=self.reviewer.user)
+        force_authenticate(self.request, user=self.reviewer)
         self.response = self.view(self.request)
 
     def test_can_access(self):
@@ -68,7 +68,7 @@ class TutorListTests(APITestCase):
 
     def test_feedback_count_matches_database(self):
         def verify_fields(tutor_obj):
-            t = Tutor.objects.get(user__username=tutor_obj['username'])
+            t = get_user_model().objects.get(username=tutor_obj['username'])
             return t.get_feedback_count() == tutor_obj['feedback_count']
 
         self.assertTrue(all(map(verify_fields, self.response.data)))
@@ -81,7 +81,7 @@ class TutorListTests(APITestCase):
 
 class TutorCreateTests(APITestCase):
 
-    USERNAME = 'some weird name!'
+    USERNAME = 'some_weird_name'
 
     @classmethod
     def setUpTestData(cls):
@@ -94,28 +94,28 @@ class TutorCreateTests(APITestCase):
                                          {'username': self.USERNAME})
         self.view = TutorApiViewSet.as_view({'post': 'create'})
 
-        force_authenticate(self.request, user=self.reviewer.user)
+        force_authenticate(self.request, user=self.reviewer)
         self.response = self.view(self.request, username=self.USERNAME)
 
     def test_can_access(self):
         self.assertEqual(self.response.status_code, status.HTTP_201_CREATED)
 
     def test_can_create(self):
-        self.assertEqual(Tutor.objects.first().user.username, self.USERNAME)
+        self.assertEqual(self.USERNAME,
+                         get_user_model().get_tutors().first().username)
 
 
 class TutorDetailViewTests(APITestCase):
 
     @classmethod
     def setUpTestData(cls):
-        cls.factory = APIClient()
         cls.user_factory = GradyUserFactory()
 
     def setUp(self):
         self.tutor = self.user_factory.make_tutor(username='fetterotto')
         self.reviewer = self.user_factory.make_reviewer()
         self.client = APIClient()
-        self.client.force_authenticate(user=self.reviewer.user)
+        self.client.force_authenticate(user=self.reviewer)
 
         url = reverse('tutor-detail', kwargs={'username': 'fetterotto'})
         self.response = self.client.get(url, format='json')
@@ -125,4 +125,4 @@ class TutorDetailViewTests(APITestCase):
 
     def test_can_view_tutor(self):
         self.assertEqual(self.response.data['username'],
-                         self.tutor.user.username)
+                         self.tutor.username)
diff --git a/core/urls.py b/core/urls.py
index 7898cedd9b7861057e317afdc17a9fe90ad1f406..e11beea8bfb4f019b0c240fd94676188582b127f 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -6,21 +6,25 @@ from core import views
 
 # Create a router and register our viewsets with it.
 router = DefaultRouter()
-router.register('student', views.StudentReviewerApiViewSet)
+router.register('student', views.StudentReviewerApiViewSet,
+                base_name='student')
 router.register('examtype', views.ExamApiViewSet)
 router.register('feedback', views.FeedbackApiView)
 router.register('submissiontype', views.SubmissionTypeApiView)
-router.register('tutor', views.TutorApiViewSet)
+router.register('tutor', views.TutorApiViewSet, base_name='tutor')
 router.register('subscription', views.SubscriptionApiViewSet)
 router.register('assignment', views.AssignmentApiViewSet)
 
 # regular views that are not viewsets
 regular_views_urlpatterns = [
-    path('student-page',
+    path('student-page/',
          views.StudentSelfApiView.as_view(),
          name='student-page'),
-    path('user-role', views.get_user_role, name='user-role'),
-    path('jwt-time-delta',
+    path('student-submissions/',
+         views.StudentSelfSubmissionsApiView.as_view(),
+         name='student-submissions'),
+    path('user-role/', views.get_user_role, name='user-role'),
+    path('jwt-time-delta/',
          views.get_jwt_expiration_delta,
          name='jwt-time-delta')
 ]
diff --git a/core/views.py b/core/views.py
index f26e69d414de17e843cdf4fea813f1228d954837..edb9840e9c1f75112696191075b0de5031b09aee 100644
--- a/core/views.py
+++ b/core/views.py
@@ -7,14 +7,16 @@ from rest_framework.decorators import api_view, detail_route
 from rest_framework.response import Response
 
 from core import models
-from core.models import (ExamType, Feedback, GeneralTaskSubscription, Student,
-                         SubmissionType, Tutor, TutorSubmissionAssignment)
+from core.models import (ExamType, Feedback, GeneralTaskSubscription,
+                         StudentInfo, SubmissionType,
+                         TutorSubmissionAssignment)
 from core.permissions import IsReviewer, IsStudent, IsTutorOrReviewer
 from core.serializers import (AssignmentDetailSerializer, AssignmentSerializer,
                               ExamSerializer, FeedbackSerializer,
-                              StudentSerializer, StudentSerializerForListView,
-                              SubmissionTypeSerializer, SubscriptionSerializer,
-                              TutorSerializer)
+                              StudentInfoSerializer,
+                              StudentInfoSerializerForListView,
+                              SubmissionSerializer, SubmissionTypeSerializer,
+                              SubscriptionSerializer, TutorSerializer)
 
 
 @api_view()
@@ -24,16 +26,15 @@ def get_jwt_expiration_delta(request):
 
 @api_view()
 def get_user_role(request):
-    return Response({'role':
-                     type(request.user.get_associated_user()).__name__})
+    return Response({'role': request.user.role})
 
 
 class StudentSelfApiView(generics.RetrieveAPIView):
     """ Gets all data that belongs to one student """
     permission_classes = (IsStudent,)
-    serializer_class = StudentSerializer
+    serializer_class = StudentInfoSerializer
 
-    def get_object(self) -> Student:
+    def get_object(self) -> StudentInfo:
         """ The object in question is the student associated with the requests
         user. Since the permission IsStudent is satisfied the member exists """
         return self.request.user.student
@@ -64,6 +65,21 @@ class FeedbackApiView(
     serializer_class = FeedbackSerializer
     lookup_field = 'submission__submission_id'
 
+    def create(self, request, *args, **kwargs):
+        serializer = self.get_serializer(
+            data={**request.data, 'of_tutor': request.user})
+        serializer.is_valid(raise_exception=True)
+
+        if serializer.data['assignment'].subscription.owner != request.user:
+            return Response({'You do not have permission to edit this'},
+                            status=status.HTTP_403_FORBIDDEN)
+
+        self.perform_create(serializer)
+        headers = self.get_success_headers(serializer.data)
+        return Response(serializer.data,
+                        status=status.HTTP_201_CREATED,
+                        headers=headers)
+
 
 class TutorApiViewSet(
         mixins.RetrieveModelMixin,
@@ -73,21 +89,17 @@ class TutorApiViewSet(
         viewsets.GenericViewSet):
     """ Api endpoint for creating, listing, viewing or deleteing tutors """
     permission_classes = (IsReviewer,)
-    queryset = Tutor.objects.all()
+    queryset = models.UserAccount.objects.filter(role='Tutor')
     serializer_class = TutorSerializer
-    lookup_field = 'user__username'
+    lookup_field = 'username'
     lookup_url_kwarg = 'username'
 
-    def perform_destroy(self, instance):
-        """ deletes the tutors account and model (on delete cascade) """
-        instance.user.delete()
-
 
 class StudentReviewerApiViewSet(viewsets.ReadOnlyModelViewSet):
     """ Gets a list of all students without individual submissions """
     permission_classes = (IsReviewer,)
-    queryset = Student.objects.all()
-    serializer_class = StudentSerializerForListView
+    queryset = StudentInfo.objects.all()
+    serializer_class = StudentInfoSerializerForListView
 
 
 class SubmissionTypeApiView(viewsets.ReadOnlyModelViewSet):
@@ -140,6 +152,21 @@ class SubscriptionApiViewSet(
         instance.delete()
         return Response(status=status.HTTP_204_NO_CONTENT)
 
+    def create(self, request, *args, **kwargs):
+        serializer = self.get_serializer(data=request.data)
+        serializer.is_valid(raise_exception=True)
+        subscription = serializer.save()
+
+        if subscription.query_type == GeneralTaskSubscription.STUDENT_QUERY:
+            subscription.reserve_all_assignments_for_a_student()
+        else:
+            subscription.get_oldest_unfinished_assignment()
+
+        headers = self.get_success_headers(serializer.data)
+        return Response(serializer.data,
+                        status=status.HTTP_201_CREATED,
+                        headers=headers)
+
 
 class AssignmentApiViewSet(
         mixins.RetrieveModelMixin,
diff --git a/delbert.py b/delbert.py
new file mode 100755
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/docs/feedback.api.json b/docs/feedback.api.json
new file mode 100644
index 0000000000000000000000000000000000000000..c48beea4146bc18419f8a5b03ecf2d9de9826fe2
--- /dev/null
+++ b/docs/feedback.api.json
@@ -0,0 +1,43 @@
+GET /subscription/<id>
+    {
+        "subscription_id": "e313e608-7453-4053-a536-5d18fc9ec3a9",
+        "owner": "reviewer01",
+        "query_type": "random",
+        "query_key": "",
+        "assignments": [
+            {
+                "assignment_id": "dbdde0d0-b1a6-474c-b2be-41edb5229803",
+                "submission_id": "1558c390-5598-482b-abd3-1f5780e75e0d",
+                "is_done": false
+            }
+        ]
+    }
+
+POST /subscription/
+    {
+        "owner": "<some user>",
+        "query_type": "random|student|submission_type|exam",
+        "query_key": "<pk for query type>?"
+    }
+
+DELETE /subscription/<id>
+PATCH /subscription/<id> {
+    "deactivate": true // or false for reactivation
+}
+
+GET /subscription/assignments/current
+GET /subscription/assignments/next
+GET /subscription/assignments/past
+
+GET /assignment/<id> // only those belonging to the requests user
+    {
+        "assignment_id": "dbdde0d0-b1a6-474c-b2be-41edb5229803",
+        "submission_id": "1558c390-5598-482b-abd3-1f5780e75e0d",
+        "is_done": false
+    }
+
+DELETE /assignment/<id> // check done conditions
+
+// done conditions
+// * feedback was posted
+// * feedback was patched (every)
diff --git a/util/factories.py b/util/factories.py
index a27f20bdede0dffea2e12b956dff1afbc33d3262..9ea00f3bd8b16329ca2680e24f7d3aa13cfdcbbf 100644
--- a/util/factories.py
+++ b/util/factories.py
@@ -3,8 +3,8 @@ import secrets
 import string
 
 from core.models import UserAccount as User
-from core.models import (ExamType, Feedback, Reviewer, Student, Submission,
-                         SubmissionType, Tutor)
+from core.models import (ExamType, Feedback, StudentInfo, Submission,
+                         SubmissionType)
 
 STUDENTS = 'students'
 TUTORS = 'tutors'
@@ -44,22 +44,35 @@ class GradyUserFactory:
     def _get_random_name(self, prefix='', suffix='', k=4):
         return ''.join((prefix, self.make_password(k), suffix))
 
-    def _make_base_user(self, username, groupname, password=None,
+    def _get_group_for_user_role(self, role):
+        """ Returns the groupname for a role """
+        return {
+            'Student': 'students',
+            'Tutor': 'tutors',
+            'Reviewer': 'reviewers'
+        }[role]
+
+    def _make_base_user(self, username, role, password=None,
                         store_pw=False, **kwargs):
         """ This is a specific wrapper for the django update_or_create method of
         objects.
-            * A new user is created and password and group are set accordingly
-            * If the user was there before password is NOT change but group is.
-            * A user must only have one group.
+            * If now username is passed, a generic one will be generated
+            * A new user is created and password and role are set accordingly
+            * If the user was there before password IS changed
+            * A user must only have one role.
 
         Returns:
-            (User object, str): The user object that was added to the group and
+            (User object, str): The user object that was added to the role and
             the password of that user if it was created.
         """
+        if not username:
+            username = self._get_random_name(prefix=role.lower() + '_')
+
         username = username.strip()
 
         user, created = User.objects.update_or_create(
             username=username,
+            role=role,
             defaults=kwargs)
 
         if created:
@@ -68,51 +81,33 @@ class GradyUserFactory:
             user.save()
 
         if created and store_pw:
-            self.password_storge(username, groupname, password)
+            self.password_storge(
+                username,
+                self._get_group_for_user_role(role),
+                password)
 
         return user
 
-    def _get_user_model_for_group(self, groupname):
-        """ Returns the model class for a usergroup """
-        return {
-            STUDENTS: Student,
-            TUTORS: Tutor,
-            REVIEWERS: Reviewer,
-        }[groupname]
-
-    def _make_user_generic(self, username, groupname, **kwargs):
-        """ Provides a model with a associated user but without any defaults
-        """
-
-        if not username:
-            username = self._get_random_name(prefix=groupname.lower() + '_')
-
-        model = self._get_user_model_for_group(groupname)
-        user = self._make_base_user(username, groupname, **kwargs)
-
-        generic_user, _ = model.objects.get_or_create(user=user)
-
-        return generic_user
-
     def make_student(self, username=None, matrikel_no=None,
                      exam=None, **kwargs):
         """ Creates a student. Defaults can be passed via kwargs like in
         relation managers objects.update method. """
-        user = self._make_user_generic(username, STUDENTS, **kwargs)
+        user = self._make_base_user(username, 'Student', **kwargs)
+        studentInfo = StudentInfo.objects.get_or_create(user=user)[0]
         if matrikel_no:
-            user.matrikel_no = matrikel_no
+            studentInfo.matrikel_no = matrikel_no
         if exam:
-            user.exam = exam
-        user.save()
+            studentInfo.exam = exam
+        studentInfo.save()
         return user
 
     def make_tutor(self, username=None, **kwargs):
         """ Creates or updates a tutor if needed with defaults """
-        return self._make_user_generic(username, TUTORS, **kwargs)
+        return self._make_base_user(username, 'Tutor', **kwargs)
 
     def make_reviewer(self, username=None, **kwargs):
         """ Creates or updates a reviewer if needed with defaults """
-        return self._make_user_generic(username, REVIEWERS, **kwargs)
+        return self._make_base_user(username, 'Reviewer', **kwargs)
 
 
 def make_exams(exams=[], **kwargs):
@@ -128,9 +123,11 @@ def make_submission_types(submission_types=[], **kwargs):
 
 
 def make_students(students=[], **kwargs):
+
     return [GradyUserFactory().make_student(
         username=student['username'],
-        exam=ExamType.objects.get(module_reference=student['exam'])
+        exam=ExamType.objects.get(
+            module_reference=student['exam']) if 'exam' in student else None
     ) for student in students]
 
 
@@ -146,7 +143,7 @@ def make_reviewers(reviewers=[], **kwargs):
 
 def make_feedback(feedback, submission_object):
     feedback['of_tutor'] = User.objects.get(
-        username=feedback['of_tutor']).get_associated_user().user
+        username=feedback['of_tutor'])
     return Feedback.objects.update_or_create(
         of_submission=submission_object,
         defaults=feedback)[0]
@@ -157,7 +154,7 @@ def make_submissions(submissions=[], **kwargs):
     for submission in submissions:
         submission_type, _ = SubmissionType.objects.get_or_create(
             name=submission.get('type', 'Auto generated type'))
-        student, _ = Student.objects.get_or_create(user=User.objects.get(
+        student, _ = StudentInfo.objects.get_or_create(user=User.objects.get(
             username=submission.get('user', 'default_user')
         ))
         submission_object, _ = Submission.objects.get_or_create(
diff --git a/util/importer.py b/util/importer.py
index d6f3080403a80c7b6f08eaa4c8e9240aa38837b9..c445545ca4f7ff13d8aa68ca69446de69028fb56 100644
--- a/util/importer.py
+++ b/util/importer.py
@@ -7,7 +7,7 @@ from typing import Callable
 import util.convert
 import util.processing
 from core.models import UserAccount as User
-from core.models import (ExamType, Feedback, Student, Submission,
+from core.models import (ExamType, Feedback, StudentInfo, Submission,
                          SubmissionType, Test)
 from util.factories import REVIEWERS, STUDENTS, TUTORS, GradyUserFactory
 from util.messages import info, warn
@@ -84,7 +84,7 @@ def add_user(username, group, **kwargs):
 def add_student(username, email, submissions, **kwargs):
 
     user = add_user(username, STUDENTS, email=email)
-    student, _ = Student.objects.update_or_create(
+    student, _ = StudentInfo.objects.update_or_create(
         user=user,
         defaults={'user': user, **kwargs}
     )