diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 3ed4bc5becc9985223978dedac71e4fdca2c1fe5..363b86959788038c4b315423fdd2cc633b5df76a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,97 +1,122 @@
 stages:
-        - build
-        - test
-        - pages
-        - staging
+  - build
+  - test
+  - pages
+  - build_image
+  - staging
 
 variables:
-        IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
+  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
 
-# ============================= Building section ============================= #
-build_backend:
-        image: docker:latest
-        stage: build
-        script:
-                - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
-                - docker build -t $IMAGE_TAG .
-                - docker push $IMAGE_TAG
+
+# ========================== Build Testing section =========================== #
+build_test_env:
+  image: python:3.6
+  stage: build
+  script:
+    - python -m venv .venv
+    - source .venv/bin/activate
+    - make install
+  artifacts:
+    paths:
+      - .venv/
+    expire_in: 1 hour
+  cache:
+    paths:
+      - .venv
 
 # ============================== Testing section ============================= #
 # ----------------------------- Backend subsection --------------------------- #
-.test_template_backend: &test_definition_backend
-        stage: test
-        image: $IMAGE_TAG
-        before_script:
-                - pip install -r requirements.dev.txt
+.test_template_virtualenv: &test_definition_virtualenv
+  image: python:3.6
+  before_script:
+    - source .venv/bin/activate
+  dependencies:
+    - build_test_env
 
 test_pytest:
-        <<: *test_definition_backend
-        services:
-                - postgres:9.5
-        script:
-                - DJANGO_SETTINGS_MODULE=grady.settings pytest --cov
-        artifacts:
-                paths:
-                        - .coverage
+  <<: *test_definition_virtualenv
+  stage: test
+  services:
+    - postgres:9.5
+  script:
+    - DJANGO_SETTINGS_MODULE=grady.settings pytest --cov
+  artifacts:
+    paths:
+      - .coverage
 
-test_prospector:
-        <<: *test_definition_backend
-        script:
-                - prospector --uses django || exit 0
+test_flake8:
+  <<: *test_definition_virtualenv
+  stage: test
+  script:
+    - flake8 --exclude=migrations --ignore=N802 core
 
 # ----------------------------- Frontend subsection -------------------------- #
 .test_template_frontend: &test_definition_frontend
-        image: node:carbon
-        stage: test
-        before_script:
-                - cd frontend/
+  image: node:carbon
+  before_script:
+    - cd frontend/
 
 test_frontend:
-        <<: *test_definition_frontend
-        script:
-                - yarn install
-                - yarn test --single-run
+  <<: *test_definition_frontend
+  stage: test
+  script:
+    - yarn install
+    - yarn test --single-run
+  cache:
+    paths:
+      - frontend/node_modules/
 
 # =========================== Gitlab pages section =========================== #
 test_coverage:
-        image: $IMAGE_TAG
-        stage:
-                pages
-        script:
-                - pip install coverage
-                - coverage html -d public
-        dependencies:
-                - test_pytest
-        artifacts:
-                paths:
-                        - public
-        only:
-                - master
+  <<: *test_definition_virtualenv
+  stage:
+    pages
+  script:
+    - coverage html -d public
+  dependencies:
+    - test_pytest
+  artifacts:
+    paths:
+      - public
+  only:
+    - master
+
+# =========================== Build Image section ============================ #
+build_backend:
+  image: docker:latest
+  stage: build_image
+  script:
+    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
+    - docker build -t $IMAGE_TAG .
+    - docker push $IMAGE_TAG
+  only:
+    - master
 
 # ============================== Staging section ============================= #
 .staging_template: &staging_definition
-        stage: staging
-        image: docker:latest
-        only:
-                - master
-        before_script:
-                - apk add --update py-pip && pip install docker-compose
+  stage: staging
+  image: docker:latest
+  only:
+    - master
+  before_script:
+    - apk add --update py-pip && pip install docker-compose
 
 staging:
-        <<: *staging_definition
-        environment:
-                name: review/$CI_COMMIT_REF_NAME
-                url: https://staging.grady.janmax.org
-                on_stop: staging_stop
-        script:
-                - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
-                - docker-compose up -d --force-recreate
+  <<: *staging_definition
+  environment:
+    name: review/$CI_COMMIT_REF_NAME
+    url: https://staging.grady.janmax.org
+    on_stop: staging_stop
+  script:
+    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
+    - docker-compose up -d --force-recreate
 
 staging_stop:
-        <<: *staging_definition
-        script:
-                - docker-compose rm --force --stop
-        when: manual
-        environment:
-                name: review/$CI_COMMIT_REF_NAME
-                action: stop
+  <<: *staging_definition
+  script:
+    - docker-compose rm --force --stop
+  when: manual
+  environment:
+    name: review/$CI_COMMIT_REF_NAME
+    action: stop
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e63378ac57329d9a0728dce6758f4d2e7585e6d0..053bd6572ce691ad5c3702e86133c104fe057666 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -12,10 +12,3 @@
     args:
     - requirements.txt
     - requirements.dev.txt
-- repo: local
-  hooks:
-  - id: prospector
-    name: prospector
-    entry: ./pre-commit-scripts/prospector.sh
-    language: script
-    types: [python]
diff --git a/Dockerfile b/Dockerfile
index bbb5fbb57527031bf4fdb3826b46f8638e41813f..3a33f19c679677ca9f9f92962b3d19e51a8c05f3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -17,9 +17,6 @@ RUN apk update \
   && apk add --virtual build-deps gcc python3-dev musl-dev curl \
   && apk add --no-cache postgresql-dev
 
-RUN mkdir -p /usr/share/dict
-RUN curl -s https://gitlab.gwdg.de/snippets/51/raw --output /usr/share/dict/words
-
 WORKDIR /code
 
 COPY . /code
diff --git a/Makefile b/Makefile
index b9dc21017bb8b37e07b0a1ee88195f1f6580b0ce..035e3be63dba808a65b7cd10049ce7e820383ce6 100644
--- a/Makefile
+++ b/Makefile
@@ -23,7 +23,7 @@ isort-check:
 migrate:
 	python manage.py migrate
 
-install:
+install: 
 	pip install -r requirements.txt
 	pip install -r requirements.dev.txt
 
diff --git a/core/grady_speak.py b/core/grady_speak.py
deleted file mode 100644
index 08b6242f7406b15b820aa11bf9ded76e07816869..0000000000000000000000000000000000000000
--- a/core/grady_speak.py
+++ /dev/null
@@ -1,23 +0,0 @@
-grady_says = [
-    "Now let's see if we can improve this with a little water, sir.",
-    "Won't keep you a moment, sir.",
-    "Grady, sir. Delbert Grady.",
-    "Yes, sir.",
-    "That's right, sir.",
-    "Why no, sir. I don't believe so.",
-    "Ah ha, it's coming off now, sir.",
-    "Why no, sir. I don't believe so.",
-    "Yes, sir.  I have a wife and two daughters, sir.",
-    "Oh, they're somewhere around.  I'm not quite sure at the moment, sir.",
-    "That's strange, sir.  I don't have any recollection of that at all.",
-    "I'm sorry to differ with you, sir, but you are the caretaker.",
-    "You have always been the caretaker, I should know, sir.",
-    "I've always been here.",
-    "Indeed, he is, Mr. Torrance. Avery willful boy. ",
-    "A rather naughty boy, if I may be so bold, sir.",
-    "Perhaps they need a good talking to, if you don't mind my saying so. Perhaps a bit more.",
-    "My girls, sir, they didn't care for the Overlook at first.",
-    "One of them actually stole a packet of matches and tried to burn it down.",
-    "But I corrected them, sir.",
-    "And when my wife tried to prevent me from doing my duty... I corrected her.",
-]
diff --git a/core/models.py b/core/models.py
index 30a240f2b5521a7e049c4663098532ae6abd3f2b..966b647d3b5aad0c0a5d6759fbddc715aa703870 100644
--- a/core/models.py
+++ b/core/models.py
@@ -13,9 +13,8 @@ from typing import Dict, Union
 from django.contrib.auth import get_user_model
 from django.contrib.auth.models import AbstractUser
 from django.db import models
-from django.db.models import Value as V
 from django.db.models import (BooleanField, Case, Count, F, IntegerField, Q,
-                              QuerySet, Sum, When)
+                              QuerySet, Sum, Value, When)
 from django.db.models.functions import Coalesce
 
 
@@ -125,7 +124,7 @@ class SubmissionType(models.Model):
                         When(
                             Q(submissions__feedback__isnull=False) &
                             Q(submissions__feedback__status=Feedback.ACCEPTED),
-                            then=V(1)), output_field=IntegerField(),
+                            then=Value(1)), output_field=IntegerField(),
                     )
                 )
             ).annotate(
@@ -230,11 +229,12 @@ class Student(models.Model):
             the annotated QuerySet as described above.
         """
         return cls.objects.annotate(
-            overall_score=Coalesce(Sum('submissions__feedback__score'), V(0)),
+            overall_score=Coalesce(Sum('submissions__feedback__score'),
+                                   Value(0)),
         ).annotate(
             done=Case(
-                When(exam__pass_score__lt=F('overall_score'), then=V(1)),
-                default=V(0),
+                When(exam__pass_score__lt=F('overall_score'), then=Value(1)),
+                default=Value(0),
                 output_field=BooleanField()
             )
         )
@@ -362,12 +362,12 @@ class Submission(models.Model):
 
         candidates = cls.objects.filter(
             (
-                Q(feedback__isnull=True)
-                | Q(feedback__origin=Feedback.DID_NOT_COMPILE)
-                | Q(feedback__origin=Feedback.COULD_NOT_LINK)
-                | Q(feedback__origin=Feedback.FAILED_UNIT_TESTS)
-            )
-            & ~Q(feedback__of_tutor=tutor)
+                Q(feedback__isnull=True) |
+                Q(feedback__origin=Feedback.DID_NOT_COMPILE) |
+                Q(feedback__origin=Feedback.COULD_NOT_LINK) |
+                Q(feedback__origin=Feedback.FAILED_UNIT_TESTS)
+            ) &
+            ~Q(feedback__of_tutor=tutor)
         )
 
         # we want a submission of a specific type
@@ -542,6 +542,7 @@ class Feedback(models.Model):
         )
         return tutor_feedback[0] if tutor_feedback else None
 
+    @classmethod
     def tutor_assigned_feedback(cls, user: Union[Tutor, Reviewer]):
         """Gets all feedback that is assigned to the tutor including
         all status cases.
diff --git a/core/serializers.py b/core/serializers.py
index f46507a58647e9e4bb5bf21705e71a84249c0725..44b91717174fe9af54cddc52ce0e668564f31b28 100644
--- a/core/serializers.py
+++ b/core/serializers.py
@@ -11,7 +11,8 @@ log = logging.getLogger(__name__)
 user_factory = GradyUserFactory()
 
 
-class DynamicFieldsModelSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
+class DynamicFieldsModelSerializer(DynamicFieldsMixin,
+                                   serializers.ModelSerializer):
     pass
 
 
diff --git a/core/tests/data_factories.py b/core/tests/data_factories.py
deleted file mode 100644
index f2f8b4f5a709718ec49cc04479d5d43ec172307e..0000000000000000000000000000000000000000
--- a/core/tests/data_factories.py
+++ /dev/null
@@ -1,85 +0,0 @@
-""" A set of factory methods that make testing easier. Each method creates all
-reuired subfields if not provided by via kwargs. """
-
-from core.models import (ExamType, Feedback, Reviewer, Student, Submission,
-                         SubmissionType, Tutor, UserAccount)
-
-# These methods are meant to be used to provide data to insert into the test
-# database
-
-
-def make_user(username='user01',
-              password='p',
-              fullname='us er01',
-              is_admin=False):
-    user = UserAccount.objects.create(username=username,
-                                      fullname=fullname,
-                                      is_admin=is_admin)
-    user.set_password(password)
-    user.save()
-    return user
-
-
-def make_exam(module_reference='TestExam B.Inf.0042',
-              total_score=42,
-              pass_score=21,
-              **kwargs):
-    return ExamType.objects.create(module_reference=module_reference,
-                                   total_score=total_score,
-                                   pass_score=pass_score, **kwargs)
-
-
-def make_submission_type(name='problem01',
-                         full_score=10,
-                         description='Very hard',
-                         solution='Impossible!'):
-    return SubmissionType.objects.create(name=name,
-                                         full_score=full_score,
-                                         description=description,
-                                         solution=solution)
-
-
-def make_student(user=None, exam=None):
-    if user is None:
-        user = make_user()
-    if exam is None:
-        exam = make_exam()
-    return Student.objects.create(user=user, exam=exam)
-
-
-def make_tutor(user=None):
-    if user is None:
-        user = make_user()
-    return Tutor.objects.create(user=user)
-
-
-def make_reviewer(user=None):
-    if user is None:
-        user = make_user()
-    return Reviewer.objects.create(user=user)
-
-
-def make_submission(type=None, student=None, text='Too hard for me ;-('):
-    if type is None:
-        type = make_submission_type()
-    if student is None:
-        student = make_student()
-    return Submission.objects.create(text=text, type=type, student=student)
-
-
-def make_feedback(of_tutor, of_submission=None, text='Very bad!', score=3):
-    if of_submission is None:
-        of_submission = make_submission()
-    return Feedback.objects.create(of_tutor=of_tutor,
-                                   of_submission=of_submission,
-                                   text=text,
-                                   score=score)
-
-
-def make_minimal_exam():
-    # also creates default examType, submissionType and student
-    submission = make_submission()
-
-    tutor = make_tutor(user=make_user(username='tutor01'))
-    feedback = make_feedback(of_tutor=tutor, of_submission=submission)
-    return submission, tutor, feedback
diff --git a/core/tests/test_student_page.py b/core/tests/test_student_page.py
index 63ae4f0af59bfacfc935c20d00e8476fdda602e8..8bd9f349564483dea02e3c4ffc927f4ec65c8e4c 100644
--- a/core/tests/test_student_page.py
+++ b/core/tests/test_student_page.py
@@ -2,9 +2,9 @@ from django.urls import reverse
 from rest_framework.test import (APIRequestFactory, APITestCase,
                                  force_authenticate)
 
-from core.models import Reviewer, SubmissionType
-from core.tests import data_factories
+from core.models import SubmissionType
 from core.views import StudentSelfApiView
+from util.factories import make_test_data
 
 
 class StudentPageTests(APITestCase):
@@ -14,11 +14,47 @@ class StudentPageTests(APITestCase):
         cls.factory = APIRequestFactory()
 
     def setUp(self):
-        self.submission, self.tutor, self.feedback = \
-            data_factories.make_minimal_exam()
-        self.student = self.submission.student
-        self.reviewer = Reviewer.objects.create(
-            user=data_factories.make_user(username='reviewer'))
+        self.test_data = make_test_data(data_dict={
+            'exams': [{
+                'module_reference': 'TestExam B.Inf.0042',
+                'total_score': 42,
+                'pass_score': 21
+            }],
+            'submission_types': [{
+                'name': 'problem01',
+                'full_score': 10,
+                'description': 'Very hard',
+                'solution': 'Impossible!'
+            }],
+            'students': [{
+                'username': 'user01',
+                'fullname': 'us er01',
+                'exam': 'TestExam B.Inf.0042'
+            }],
+            'tutors': [{
+                'username': 'tutor01'
+            }],
+            'reviewers': [{
+                'username': 'reviewer'
+            }],
+            'submissions': [{
+                'user': 'user01',
+                'type': 'problem01',
+                'text': 'Too hard for me ;-(',
+                'feedback': {
+                    'of_tutor': 'tutor01',
+                    'text': 'Very bad!',
+                    'score': 3
+                }
+            }]
+        })
+
+        self.student = self.test_data['students'][0]
+        self.tutor = self.test_data['tutors'][0]
+        self.reviewer = self.test_data['reviewers'][0]
+        self.submission = self.test_data['submissions'][0]
+        self.feedback = self.submission.feedback
+
         self.request = self.factory.get(reverse('student-page'))
         self.view = StudentSelfApiView.as_view()
 
diff --git a/core/tests/test_student_reviewer_viewset.py b/core/tests/test_student_reviewer_viewset.py
index cf30b662f022b649f6b33b302e737c9b5ea9cab3..d486641ce9bcb6a9e936988ca2b4a5bd5d10e4fd 100644
--- a/core/tests/test_student_reviewer_viewset.py
+++ b/core/tests/test_student_reviewer_viewset.py
@@ -3,9 +3,8 @@ from rest_framework import status
 from rest_framework.test import (APIRequestFactory, APITestCase,
                                  force_authenticate)
 
-from core.tests import data_factories
 from core.views import StudentReviewerApiViewSet
-from util.factories import GradyUserFactory
+from util.factories import make_test_data
 
 
 class StudentPageTests(APITestCase):
@@ -13,12 +12,46 @@ class StudentPageTests(APITestCase):
     @classmethod
     def setUpTestData(cls):
         cls.factory = APIRequestFactory()
-        cls.user_factory = GradyUserFactory()
 
     def setUp(self):
-        self.submission, _, _ = data_factories.make_minimal_exam()
-        self.student = self.submission.student
-        self.reviewer = self.user_factory.make_reviewer(username='reviewer')
+        self.test_data = make_test_data(data_dict={
+            'exams': [{
+                'module_reference': 'TestExam B.Inf.0042',
+                'total_score': 42,
+                'pass_score': 21
+            }],
+            'submission_types': [{
+                'name': 'problem01',
+                'full_score': 10,
+                'description': 'Very hard',
+                'solution': 'Impossible!'
+            }],
+            'students': [{
+                'username': 'user01',
+                'fullname': 'us er01',
+                'exam': 'TestExam B.Inf.0042'
+            }],
+            'tutors': [{
+                'username': 'tutor'
+            }],
+            'reviewers': [{
+                'username': 'reviewer'
+            }],
+            'submissions': [{
+                'user': 'user01',
+                'type': 'problem01',
+                'text': 'Too hard for me ;-(',
+                'feedback': {
+                    'score': 3,
+                    'of_tutor': 'tutor'
+                }
+            }]
+        })
+
+        self.student = self.test_data['students'][0]
+        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'})
 
diff --git a/core/urls.py b/core/urls.py
index d0e251b70cbc95adbd26041e484dcc9b643c1c4e..cb66e7d2222fa0fd4a9706a00c8bac8339c93646 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -15,9 +15,11 @@ router.register(r'tutor', views.TutorApiViewSet)
 
 # regular views that are not viewsets
 regular_views_urlpatterns = [
-    url(r'student-page', views.StudentSelfApiView.as_view(), name='student-page'),
+    url(r'student-page', views.StudentSelfApiView.as_view(),
+        name='student-page'),
     url(r'user-role', views.get_user_role, name='user-role'),
-    url(r'jwt-time-delta', views.get_jwt_expiration_delta, name='jwt-time-delta')
+    url(r'jwt-time-delta', views.get_jwt_expiration_delta,
+        name='jwt-time-delta')
 ]
 
 urlpatterns = [
diff --git a/core/views.py b/core/views.py
index 32bd7f1263e3e4bedb6719e171846e7b356d1da5..6d5a9595d19d6c8451c84da2f92c6f2a674c0dcc 100644
--- a/core/views.py
+++ b/core/views.py
@@ -20,7 +20,8 @@ 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':
+                     type(request.user.get_associated_user()).__name__})
 
 
 class StudentSelfApiView(generics.RetrieveAPIView):
diff --git a/pre-commit-scripts/prospector.sh b/pre-commit-scripts/prospector.sh
deleted file mode 100755
index f6d218ff3bdf21de8df9dc93d0cbf80bcfd80452..0000000000000000000000000000000000000000
--- a/pre-commit-scripts/prospector.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-diff_files="$(git diff  --cached --name-only --relative --diff-filter=AM)"
-if [ -n "$diff_files" ]; then
-	prospector --uses django $diff_files
-else
-	exit 0
-fi
diff --git a/requirements.dev.txt b/requirements.dev.txt
index 63b69b7d1ecfc80af3daa2c7027332dadfc8178a..eaa909a4ee73cc11378a4b7d2041c5d66d204e48 100644
--- a/requirements.dev.txt
+++ b/requirements.dev.txt
@@ -1,4 +1,4 @@
+flake8~=3.5.0
 pre-commit~=1.4.1
-prospector~=0.12.7
 pytest-cov~=2.5.1
 pytest-django~=3.1.2
diff --git a/util/factories.py b/util/factories.py
index a4161cfd62634c76c0e3480571b4f0f421455c6a..5b6f71575d7b426e5b8dc95048ffccb84034b3bb 100644
--- a/util/factories.py
+++ b/util/factories.py
@@ -1,8 +1,10 @@
 import configparser
 import secrets
+import string
 
+from core.models import (Reviewer, Student, Tutor, ExamType,
+                         SubmissionType, Submission, Feedback)
 from core.models import UserAccount as User
-from core.models import Reviewer, Student, Tutor
 
 STUDENTS = 'students'
 TUTORS = 'tutors'
@@ -11,12 +13,10 @@ REVIEWERS = 'reviewers'
 PASSWORDS = '.importer_passwords'
 
 
-def get_xkcd_password(k=2):
-    with open('/usr/share/dict/words') as words:
-        choose_from = list({word.strip().lower()
-                            for word in words if 5 < len(word) < 8})
-
-    return ''.join(secrets.choice(choose_from) for _ in range(k))
+def get_random_password(length=32):
+    """ Returns a cryptographically random string of specified length """
+    return ''.join(secrets.choice(string.ascii_lowercase)
+                   for _ in range(length))
 
 
 def store_password(username, groupname, password):
@@ -35,7 +35,7 @@ def store_password(username, groupname, password):
 class GradyUserFactory:
 
     def __init__(self,
-                 password_generator_func=get_xkcd_password,
+                 password_generator_func=get_random_password,
                  password_storge=store_password,
                  *args, **kwargs):
         self.password_generator_func = password_generator_func
@@ -43,9 +43,9 @@ class GradyUserFactory:
 
     @staticmethod
     def _get_random_name(prefix='', suffix='', k=1):
-        return ''.join((prefix, get_xkcd_password(k), suffix))
+        return ''.join((prefix, get_random_password(k), suffix))
 
-    def _make_base_user(self, username, groupname, store_pw=False, **kwargs):
+    def _make_base_user(self, username, groupname, 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
@@ -63,7 +63,7 @@ class GradyUserFactory:
             defaults=kwargs)
 
         if created:
-            password = self.password_generator_func()
+            password = self.password_generator_func() if password is None else password
             user.set_password(password)
             user.save()
 
@@ -99,9 +99,10 @@ class GradyUserFactory:
         relation managers objects.update method. """
         user = self._make_user_generic(username, STUDENTS, **kwargs)
         if matrikel_no:
-            user.objects.update(matrikel_no=matrikel_no)
+            user.matrikel_no = matrikel_no
         if exam:
-            user.objects.update(exam=exam)
+            user.exam = exam
+        user.save()
         return user
 
     def make_tutor(self, username=None, **kwargs):
@@ -111,3 +112,162 @@ class GradyUserFactory:
     def make_reviewer(self, username=None, **kwargs):
         """ Creates or updates a reviewer if needed with defaults """
         return self._make_user_generic(username, REVIEWERS, **kwargs)
+
+
+def make_exams(exams=[], **kwargs):
+    return [ExamType.objects.get_or_create(
+        module_reference=exam['module_reference'],
+        defaults=exam)[0] for exam in exams]
+
+
+def make_submission_types(submission_types=[], **kwargs):
+    return [SubmissionType.objects.get_or_create(
+        name=submission_type['name'], defaults=submission_type)[0]
+            for submission_type in submission_types]
+
+
+def make_students(students=[], **kwargs):
+    return [GradyUserFactory().make_student(
+        username=student['username'],
+        exam=ExamType.objects.get(module_reference=student['exam'])
+    ) for student in students]
+
+
+def make_tutors(tutors=[], **kwargs):
+    return [GradyUserFactory().make_tutor(**tutor)
+            for tutor in tutors]
+
+
+def make_reviewers(reviewers=[], **kwargs):
+    return [GradyUserFactory().make_reviewer(**reviewer)
+            for reviewer in reviewers]
+
+
+def make_feedback(feedback, submission_object):
+    tutor = User.objects.get(
+        username=feedback['of_tutor']).get_associated_user()
+    return Feedback.objects.update_or_create(
+        of_submission=submission_object,
+        of_tutor=tutor,
+        defaults={
+            'text': feedback.get('text', ''),
+            'score': feedback['score']
+        })[0]
+
+
+def make_submissions(submissions=[], **kwargs):
+    submission_objects = []
+    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(
+            username=submission.get('user', 'default_user')
+        ))
+        submission_object, _ = Submission.objects.get_or_create(
+            type=submission_type, student=student, defaults={
+                'seen_by_student': submission.get('seen_by_student', False),
+                'text': submission.get('text', ''),
+            })
+        if 'feedback' in submission:
+            make_feedback(submission['feedback'], submission_object)
+        submission_objects.append(submission_object)
+    return submission_objects
+
+
+def make_test_data(data_dict):
+    return {
+        'exams': make_exams(**data_dict),
+        'submission_types': make_submission_types(**data_dict),
+        'students': make_students(**data_dict),
+        'tutors': make_tutors(**data_dict),
+        'reviewers': make_reviewers(**data_dict),
+        'submissions': make_submissions(**data_dict)
+    }
+
+
+def init_test_instance():
+    return make_test_data(
+        data_dict={
+            'exams': [{
+                'module_reference': 'Test Exam 01',
+                'total_score': 100,
+                'pass_score': 60,
+            }],
+            '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',
+                'exam': 'Test Exam 01',
+            }],
+            'tutors': [{
+                'username': 'tutor01'
+            }],
+            'reviewers': [{
+                'username': 'reviewer01'
+            }],
+            'submissions': [
+                {
+                    'text': 'function blabl\n'
+                            '   on multi lines\n'
+                            '       for blabla in bla:\n'
+                            '   arrrgh\n'
+                            '       asasxasx\n'
+                            '           lorem ipsum und so\n',
+                    'type': '01. Sort this or that',
+                    'user': 'student01',
+                    'feedback': {
+                        'text': 'Not good!',
+                        'score': 5,
+                        'of_tutor': 'tutor01',
+                    }
+                },
+                {
+                    'text': 'function blabl\n'
+                            '   on multi lines\n'
+                            '       for blabla in bla:\n'
+                            '   arrrgh\n'
+                            '       asasxasx\n'
+                            '           lorem ipsum und so\n',
+                    'type': '02. Merge this or that or maybe even this',
+                    'user': 'student01',
+                    'feedback': {
+                        'text': 'A little bit better!',
+                        'score': 10,
+                        'of_tutor': 'tutor01',
+                    },
+                },
+                {
+                    'text': 'function blabl\n'
+                            '   on multi lines\n'
+                            '       for blabla in bla:\n'
+                            '   arrrgh\n'
+                            '       asasxasx\n'
+                            '           lorem ipsum und so\n',
+                    'type': '03. This one exists for the sole purpose to test',
+                    'user': 'student01',
+                    'feedback': {
+                        'text': 'Awesome!',
+                        'score': 30,
+                        'of_tutor': 'tutor01',
+                    },
+                },
+            ]}
+    )