diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py
index 0a1832a515a9b716324bfae5134e2c92024de6d1..41d82568a409984d883a048591eb3171deb98e6a 100644
--- a/core/migrations/0001_initial.py
+++ b/core/migrations/0001_initial.py
@@ -2,10 +2,11 @@
 # Generated by Django 1.10.6 on 2017-04-05 20:11
 from __future__ import unicode_literals
 
-import core.models
+import django.db.models.deletion
 from django.conf import settings
 from django.db import migrations, models
-import django.db.models.deletion
+
+import core.models
 
 
 class Migration(migrations.Migration):
diff --git a/core/migrations/0002_auto_20170412_1447.py b/core/migrations/0002_auto_20170412_1447.py
index 592dc9d2199eb7dc7250b274b44dd3bbba027ae6..f92b185386910b6e770fa8403e32b8b2502ba24d 100644
--- a/core/migrations/0002_auto_20170412_1447.py
+++ b/core/migrations/0002_auto_20170412_1447.py
@@ -2,8 +2,8 @@
 # Generated by Django 1.10.7 on 2017-04-12 14:47
 from __future__ import unicode_literals
 
-from django.db import migrations, models
 import django.utils.timezone
+from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
diff --git a/core/migrations/0005_auto_20170413_0124.py b/core/migrations/0005_auto_20170413_0124.py
index 571477e152adb23bfe722f5ceeb6c98c2dd9c7ab..b25f249e46b66aab720450b013058e13bc31beda 100644
--- a/core/migrations/0005_auto_20170413_0124.py
+++ b/core/migrations/0005_auto_20170413_0124.py
@@ -2,8 +2,8 @@
 # Generated by Django 1.10.7 on 2017-04-13 01:24
 from __future__ import unicode_literals
 
-from django.db import migrations, models
 import django.db.models.deletion
+from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
diff --git a/core/migrations/0007_auto_20170522_1827.py b/core/migrations/0007_auto_20170522_1827.py
index f69800a37fe4bdb41c7f1d037dc4d2eb2e077b7a..fb6c0578ab3202c63ea9376750c9fff4fc7b54af 100644
--- a/core/migrations/0007_auto_20170522_1827.py
+++ b/core/migrations/0007_auto_20170522_1827.py
@@ -2,8 +2,8 @@
 # Generated by Django 1.10.7 on 2017-05-22 18:27
 from __future__ import unicode_literals
 
-from django.db import migrations, models
 import django.db.models.deletion
+from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
diff --git a/core/migrations/0010_auto_20170710_1604.py b/core/migrations/0010_auto_20170710_1604.py
index 319da912d39b54051376e985c63dc8407adf5c0d..ab702ede9c84a59e0762885ed0aaef9d40d5ae2d 100644
--- a/core/migrations/0010_auto_20170710_1604.py
+++ b/core/migrations/0010_auto_20170710_1604.py
@@ -2,8 +2,8 @@
 # Generated by Django 1.10.7 on 2017-07-10 16:04
 from __future__ import unicode_literals
 
-from django.db import migrations, models
 import django.db.models.deletion
+from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
diff --git a/core/migrations/0012_auto_20170711_1104.py b/core/migrations/0012_auto_20170711_1104.py
index ca27ba4ce3c4c3b614a893878768bc9b02c9765e..9cc1976430b11665b40073f68b976860fd163598 100644
--- a/core/migrations/0012_auto_20170711_1104.py
+++ b/core/migrations/0012_auto_20170711_1104.py
@@ -2,8 +2,8 @@
 # Generated by Django 1.10.7 on 2017-07-11 11:04
 from __future__ import unicode_literals
 
-from django.db import migrations, models
 import django.db.models.deletion
+from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
diff --git a/core/migrations/0013_auto_20170712_1643.py b/core/migrations/0013_auto_20170712_1643.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd526207077997edd6dd8b0d2277dd1537d96f66
--- /dev/null
+++ b/core/migrations/0013_auto_20170712_1643.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-07-12 16:43
+from __future__ import unicode_literals
+
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0012_auto_20170711_1104'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='examtype',
+            name='module_reference',
+            field=models.CharField(max_length=50, unique=True),
+        ),
+        migrations.AlterField(
+            model_name='student',
+            name='user',
+            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='student', to=settings.AUTH_USER_MODEL),
+        ),
+    ]
diff --git a/core/migrations/0014_auto_20170712_1704.py b/core/migrations/0014_auto_20170712_1704.py
new file mode 100644
index 0000000000000000000000000000000000000000..65a9c943d2aa857d13e44a062bacf5b24629bd5b
--- /dev/null
+++ b/core/migrations/0014_auto_20170712_1704.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-07-12 17:04
+from __future__ import unicode_literals
+
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0013_auto_20170712_1643'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='feedback',
+            name='of_tutor',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='feedback_list', to=settings.AUTH_USER_MODEL),
+        ),
+    ]
diff --git a/core/models.py b/core/models.py
index 8f417ff80cd5967f47be54d862c562b9a30d7f5a..6801e33b57afcd821a58b8b0ddaac94956c5a884 100644
--- a/core/models.py
+++ b/core/models.py
@@ -41,13 +41,15 @@
 ##########################################################################
 
 
-from random import sample, randrange
-from string import ascii_lowercase
 from collections import OrderedDict
+from random import randrange, sample
+from string import ascii_lowercase
 
 from django.contrib.auth.models import User
 from django.db import models
-from django.db.models import Q, F, Sum, Value as V, When, Case, BooleanField
+from django.db.models import Value as V
+from django.db.models import (BooleanField, Case, Count, F, IntegerField, Q,
+                              Sum, When)
 from django.db.models.functions import Coalesce
 
 SLUG_LENGTH = 16
@@ -61,6 +63,13 @@ def random_matrikel_no():
     return str(2e7 + randrange(1e8))
 
 
+def get_annotated_tutor_list():
+    return User.objects\
+        .annotate(Count('corrected_submissions'))\
+        .filter(groups__name='Tutors')\
+        .order_by('-corrected_submissions__count')
+
+
 class ExamType(models.Model):
 
     class Meta:
@@ -92,6 +101,39 @@ class SubmissionType(models.Model):
         verbose_name        = "SubmissionType"
         verbose_name_plural = "SubmissionType Set"
 
+    @classmethod
+    def get_annotated_feedback_count(cls):
+        """ Annotates submission lists with counts
+
+        count both
+            * number of submission per submission type
+            * count of received feedback per submission type
+            *
+        Alternative with case
+            Count(Case(
+                When(submissions__feedback_list__origin=Feedback.MANUAL,
+                    then=Value(1)), output_field=IntegerField())
+            )
+
+        Returns:
+            annotated queryset
+        """
+        return cls.objects\
+            .annotate( # to display only manual
+                feedback_count=Count(
+                    Case(
+                        When(
+                                Q(submissions__feedback__isnull=False) &
+                                Q(submissions__feedback__status=Feedback.ACCEPTED),
+                            then=V(1)), output_field=IntegerField(),
+                    )
+                )
+            ).annotate(
+                submission_count=Count('submissions')
+            ).annotate(
+                percentage=(F('feedback_count') * 100 / F('submission_count'))
+            ).all().order_by('name')
+
 
 class Student(models.Model):
     # Fields
@@ -102,6 +144,7 @@ class Student(models.Model):
         unique=True, max_length=8, default=random_matrikel_no)
     user = models.OneToOneField(
         User, on_delete=models.CASCADE,
+        related_name='student',
         limit_choices_to={'groups__name': 'Students'},
     )
 
@@ -128,15 +171,6 @@ class Student(models.Model):
             )
         )
 
-    def overall_score(self): # TODO purge
-        return sum(self.score_per_submission().values())
-
-    def has_passed_exam(self):
-        return self.overall_score >= self.exam.pass_score
-
-    def has_pass_or_fail_exam(self):
-        return self.exam.pass_only
-
     def disable(self):
         self.has_logged_in = True
         self.save()
@@ -263,7 +297,7 @@ class Feedback(models.Model):
         unique=True,
         blank=False, null=False)
     of_tutor = models.ForeignKey(
-        User, related_name='corrected_submissions',)
+        User, related_name='feedback_list',)
     of_reviewer = models.ForeignKey(
         User,
         related_name='reviewed_submissions',
@@ -320,6 +354,14 @@ class Feedback(models.Model):
     def get_full_score(self):
         return self.of_submission.type.full_score
 
+    @classmethod
+    def get_open_feedback(cls, user):
+        return cls.objects.filter(
+            Q(status=Feedback.OPEN) &
+            ~Q(of_tutor=user) # you shall not request your own feedback
+        )
+
+
     @classmethod
     def tutor_unfinished_feedback(cls, user):
         """Gets only the feedback that is assigned and not accepted. A tutor
diff --git a/core/templates/core/r/reviewer_base.html b/core/templates/core/r/reviewer_base.html
index f2e5e46f9d01d24e2b65e4a28986f7f3d60012f4..c79f575f6e3321ba897604d6c1dd143394a65e58 100644
--- a/core/templates/core/r/reviewer_base.html
+++ b/core/templates/core/r/reviewer_base.html
@@ -4,8 +4,8 @@
 
 {% block navbar %}
 <a class="nav-item nav-link" href="{% url 'start' %}">Feedback</a>
-<a class="nav-item nav-link" href="{% url 'submission_list' %}">Submissions</a>
-<a class="nav-item nav-link" href="{% url 'student_list' %}">Students</a>
+<a class="nav-item nav-link" href="{% url 'ReviewerSubmissionListView' %}">Submissions</a>
+<a class="nav-item nav-link" href="{% url 'ReviewerStudentListView' %}">Students</a>
 <div class="nav-item dropdown">
   <a class="nav-link dropdown-toggle" href="http://example.com" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
     Export
diff --git a/core/templates/core/r/student_list.html b/core/templates/core/r/student_list.html
index 4d4837216ba95aa407d95bea01f1c6afd236de6a..57f433f05720ecc6e29f96ea93772ad86635c508 100644
--- a/core/templates/core/r/student_list.html
+++ b/core/templates/core/r/student_list.html
@@ -28,9 +28,9 @@
           <td>{{student.exam}}</td>
           {% for sub in student.submissions.all %}
             <td>{% if sub.feedback %}
-              <a href="{% url 'SubmissionViewReviewer' sub.slug %}" role="button" class="btn-link btn-sm"><code>{{sub.feedback.score}} / {{sub.type.full_score}}</code></a>
+              <a href="{% url 'ReviewerSubmissionView' sub.slug %}" role="button" class="btn-link btn-sm"><code>{{sub.feedback.score}} / {{sub.type.full_score}}</code></a>
             {% else %}
-              <a href="{% url 'SubmissionViewReviewer' sub.slug %}" role="button" class="btn btn-outline-primary btn-sm">View</a>
+              <a href="{% url 'ReviewerSubmissionView' sub.slug %}" role="button" class="btn btn-outline-primary btn-sm">View</a>
             {% endif %} </td>
           {% endfor %}
           <td><code>{{student.overall_score}}</code></td>
diff --git a/core/templates/core/r/student_submission_list.html b/core/templates/core/r/student_submission_list.html
index 59bea6014bdaabec8d182e335d77db81e34ed8be..484bdd8e06323d57ae8256cd09ad8ef8f53fe145 100644
--- a/core/templates/core/r/student_submission_list.html
+++ b/core/templates/core/r/student_submission_list.html
@@ -21,7 +21,7 @@
       <tbody>
         {% for submission in submission_list %}
         <tr>
-          <td class="align-middle fit"> <a href="{% url 'SubmissionViewReviewer' submission.slug %}" class="btn btn-outline-primary mb-1" name="edit" value="View">View submission</a></td>
+          <td class="align-middle fit"> <a href="{% url 'ReviewerSubmissionView' submission.slug %}" class="btn btn-outline-primary mb-1" name="edit" value="View">View submission</a></td>
           <td class="align-middle"> {{ submission.type }} </td>
           <td class="align-middle"> {{ submission.student }} </td>
           <td class="align-middle fit">
diff --git a/core/templates/core/s/student_startpage.html b/core/templates/core/s/student_startpage.html
index 9ee3bb14f84c78314c88b3265fb712efc1e1aa84..cd016dd7f5b03e445b895b4b2149f082118e580c 100644
--- a/core/templates/core/s/student_startpage.html
+++ b/core/templates/core/s/student_startpage.html
@@ -5,11 +5,10 @@
 {% block navbar %} Student Exam View {% endblock navbar %}
 
 {% block body_block %}
-
 <div class="row justify-content-center">
   <div class="col-6">
     <div class="row my-3">
-      <h2>Hello {{ student.student }}</h2>
+      <h2>Submissions of {{ student.name }}</h2>
     </div>
 
     <div class="row my-2">
@@ -21,7 +20,7 @@
           <th></th>
         </thead>
         <tbody>
-          {% for submission in submission_list %}
+          {% for submission in student.submissions.all %}
           <tr class="align-middle">
             <td class="align-middle">
               {% if submission.seen_by_student %}
@@ -38,7 +37,7 @@
               {% endif %}
               {% endwith %}
             </td>
-            <td class="align-middle"><a class="btn btn-primary" href="{% url 'SubmissionViewStudent' submission.slug %}">View</a></td>
+            <td class="align-middle"><a class="btn btn-primary" href="{% url 'StudentSubmissionView' submission.slug %}">View</a></td>
           </tr>
           {% endfor %}
         </tbody>
@@ -46,7 +45,4 @@
     </div>
   </div>
 </div>
-
-
-
 {% endblock body_block %}
diff --git a/core/templates/core/t/tutor_startpage.html b/core/templates/core/t/tutor_startpage.html
index c9c3277ad5f2ca06d345280e6b3e6cc24b5d240a..52836ebf0bb4bb65d215a602108ca300b513f097 100644
--- a/core/templates/core/t/tutor_startpage.html
+++ b/core/templates/core/t/tutor_startpage.html
@@ -38,7 +38,7 @@
         <tbody>
           <tr>
             <td class="fit"><strong>Your contribution:</strong></td>
-            <td colspan="6"><code>{% if feedback_list|length > 0 %} {{feedback_list|length}} {% else %} None. Sad. {% endif %}</code></td>
+            <td colspan="6"><code>{% if tutor.feedback_list.all|length > 0 %} {{tutor.feedback_list.all|length}} {% else %} None. Sad. {% endif %}</code></td>
           </tr>
         </tbody>
       </table>
@@ -88,7 +88,7 @@
           </tr>
         </thead>
         <tbody>
-          {% for feedback in feedback_list %}
+          {% for feedback in tutor.feedback_list.all %}
           <tr>
             <td>
               {% include "core/component/feedback_badge.html" %}
diff --git a/core/urls.py b/core/urls.py
index 4c36c25c43a46df9a68316a5114d4b5b40ff89e4..867d523a9e2629c36d74dcdda61c6a9514a50d8c 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -14,13 +14,13 @@ urlpatterns = [
     url(r'^feedback/edit/(?P<feedback_slug>\w+)/$', views.FeedbackEdit.as_view(), name='FeedbackEdit'),
     url(r'^feedback/delete/(?P<feedback_slug>\w+)/$', views.delete_feedback, name='FeedbackDelete'),
 
-    url(r'^r/studentlist/$', views.StudentListView.as_view(), name='student_list'),
-    url(r'^r/submission/list/$', views.get_submission_list, name='submission_list'),
-    url(r'^r/submission/view/(?P<slug>\w+)/$', views.SubmissionViewReviewer.as_view(), name='SubmissionViewReviewer'),
+    url(r'^r/student/list/$', views.ReviewerStudentListView.as_view(), name='ReviewerStudentListView'),
+    url(r'^r/submission/list/$', views.ReviewerSubmissionListView.as_view(), name='ReviewerSubmissionListView'),
+    url(r'^r/submission/view/(?P<slug>\w+)/$', views.ReviewerSubmissionView.as_view(), name='ReviewerSubmissionView'),
     url(r'^r/submission/create-feedback-for/(?P<slug>\w+)/$', views.create_feedback_for_submission, name='create_feedback_for_submission'),
     url(r'^r/submission/download/(?P<slug>\w+)/$', views.download_submissions, name='download_submissions'),
 
-    url(r'^s/submission/view/(?P<slug>\w+)/$', views.SubmissionViewStudent.as_view(), name='SubmissionViewStudent'),
+    url(r'^s/submission/view/(?P<slug>\w+)/$', views.StudentSubmissionView.as_view(), name='StudentSubmissionView'),
 
     url(r'^csv/$', views.export_csv, name='export')
 ]
diff --git a/core/views/__init__.py b/core/views/__init__.py
index 9c990fbac6cd13d181bd5e7d190e563dc61067e2..67868d83114fb61ae463b7c2cd5b9bb0127f5bb1 100644
--- a/core/views/__init__.py
+++ b/core/views/__init__.py
@@ -1,6 +1,9 @@
 from .login import *
 from .feedback import *
+from .generics import *
+
 from .submission import *
 from .user_startpages import *
 from .index import *
 from .export_csv import *
+
diff --git a/core/views/export_csv.py b/core/views/export_csv.py
index 93dbffda982558fb661d244a1fc38e6dacb38ae9..1e1a7fdd12d212085b0f36a0bef223019bdd2106 100644
--- a/core/views/export_csv.py
+++ b/core/views/export_csv.py
@@ -1,8 +1,9 @@
 import csv
+
 from django.http import HttpResponse
 
-from core.models import Student, SubmissionType
 from core.custom_annotations import group_required
+from core.models import Student, SubmissionType
 
 
 @group_required('Reviewers')
@@ -15,12 +16,12 @@ def export_csv(request):
     writer.writerow(['Matrikel', 'Username',  'Name', 'Sum'] +
                     [s.name for s in SubmissionType.objects.all().order_by('name')])
 
-    for student in Student.objects.all():
+    for student in Student.get_overall_score_annotated_submission_list():
         writer.writerow([
             student.matrikel_no,
             student.user.username,
             student.name,
-            student.overall_score(),
+            student.overall_score,
             *student.score_per_submission().values()
         ])
 
diff --git a/core/views/feedback.py b/core/views/feedback.py
index 5c60bc4e254c638feede522bbc7906ff1b613861..db95a26d8457c2286f871350787b1fa5846f4b9b 100644
--- a/core/views/feedback.py
+++ b/core/views/feedback.py
@@ -1,7 +1,7 @@
 from random import choice
 
 from django.contrib import messages
-from django.http import Http404, HttpResponseRedirect, HttpResponse
+from django.http import Http404, HttpResponse, HttpResponseRedirect
 from django.urls import reverse
 from django.utils.decorators import method_decorator
 from django.views.generic.edit import UpdateView
diff --git a/core/views/generics.py b/core/views/generics.py
new file mode 100644
index 0000000000000000000000000000000000000000..6afd8c675c3dc4707226755f5d773e2852fedf20
--- /dev/null
+++ b/core/views/generics.py
@@ -0,0 +1,58 @@
+from django.utils.decorators import method_decorator
+from django.views.generic import DetailView, ListView, View
+
+from core.custom_annotations import group_required
+from core.models import SubmissionType, get_annotated_tutor_list
+
+
+class StudentView(View):
+
+    @method_decorator(group_required('Students',))
+    def dispatch(self, *args, **kwargs):
+        return super().dispatch(*args, **kwargs)
+
+
+class StudentListView(StudentView, ListView):
+    pass
+
+
+class StudentDetailView(StudentView, DetailView):
+    pass
+
+
+class TutorView(View):
+
+    @method_decorator(group_required('Tutors',))
+    def dispatch(self, *args, **kwargs):
+        return super().dispatch(*args, **kwargs)
+
+
+class TutorListView(TutorView, ListView):
+    pass
+
+
+class TutorDetailView(TutorView, DetailView):
+    pass
+
+
+class ReviewerView(View):
+
+    @method_decorator(group_required('Reviewers',))
+    def dispatch(self, *args, **kwargs):
+        return super().dispatch(*args, **kwargs)
+
+
+class ReviewerListView(ReviewerView, ListView):
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+
+        return {
+            'submission_type_list': SubmissionType.get_annotated_feedback_count(),
+            'tutor_list':           get_annotated_tutor_list(),
+            **context,
+        }
+
+
+class ReviewerDetailView(ReviewerView, DetailView):
+    pass
diff --git a/core/views/login.py b/core/views/login.py
index 4ebc8c7620d762bce09e61acd43440443c571bbd..708a1a89a1f8e67add915f8541d86dbb64111e02 100644
--- a/core/views/login.py
+++ b/core/views/login.py
@@ -1,12 +1,11 @@
+from django.contrib import messages
 from django.contrib.auth import authenticate, login, logout
 from django.contrib.auth.decorators import login_required
-from django.contrib import messages
 from django.http import HttpResponseRedirect
 from django.urls import reverse
 
 from core.custom_annotations import in_groups
 
-
 __all__ = ('user_login', 'user_logout')
 
 def is_disabled(user):
diff --git a/core/views/submission.py b/core/views/submission.py
index fc6a14bb4a935f9b69f218f7f7a0133e0e5cc120..8d7eff78bfe5f9a5572f091d1c2284826a87916d 100644
--- a/core/views/submission.py
+++ b/core/views/submission.py
@@ -1,23 +1,15 @@
-from django.utils.decorators import method_decorator
-from django.views.generic import DetailView, ListView
-from django.shortcuts import render
-from django.db.models import Sum
+from django.views.generic import DetailView
 
+from core.custom_annotations import in_groups
+from core.models import Feedback, Student, Submission
+from core.views.generics import (ReviewerDetailView, ReviewerListView,
+                                 StudentView)
 
-from core.custom_annotations import group_required, in_groups
-from core.models import Submission, Feedback, Student
 
-from .user_startpages import get_annotated_feedback_count, get_annotated_tutor_list
+class StudentSubmissionView(StudentView, DetailView):
 
-
-class SubmissionViewStudent(DetailView):
-
-    template_name = 'core/s/single_submission.html'
-    model = Submission
-
-    @method_decorator(group_required('Students',))
-    def dispatch(self, *args, **kwargs):
-        return super(SubmissionViewStudent, self).dispatch(*args, **kwargs)
+    template_name   = 'core/s/single_submission.html'
+    model           = Submission
 
     def get_object(self):
         obj = Submission.objects.get(slug=self.kwargs['slug'])
@@ -27,49 +19,27 @@ class SubmissionViewStudent(DetailView):
         return obj
 
 
-class SubmissionViewReviewer(DetailView):
+class ReviewerSubmissionView(ReviewerDetailView):
 
+    model         = Submission
     template_name = 'core/r/single_submission.html'
-    model = Submission
-
-    @method_decorator(group_required('Reviewers',))
-    def dispatch(self, *args, **kwargs):
-        return super(SubmissionViewReviewer, self).dispatch(*args, **kwargs)
 
     def get_object(self):
         return Submission.objects.get(slug=self.kwargs['slug'])
 
 
-@group_required('Reviewers')
-def get_submission_list(request):
-    context = {
-        'submission_list': Submission.objects.all(),
-        'submission_type_list': get_annotated_feedback_count(),
-        'tutor_list': get_annotated_tutor_list(),
-    }
-    return render(request, 'core/r/student_submission_list.html', context)
+class ReviewerSubmissionListView(ReviewerListView):
+
+    model               = Submission
+    template_name       = 'core/r/student_submission_list.html'
+    context_object_name = 'submission_list'
 
 
-class StudentListView(ListView):
+class ReviewerStudentListView(ReviewerListView):
 
     model               = Student
     template_name       = 'core/r/student_list.html'
     context_object_name = 'student_list'
 
-    @method_decorator(group_required('Reviewers',))
-    def dispatch(self, *args, **kwargs):
-        return super().dispatch(*args, **kwargs)
-
     def get_queryset(self):
         return self.model.get_overall_score_annotated_submission_list()
-
-    def get_context_data(self, **kwargs):
-        context = super().get_context_data(**kwargs)
-
-        context = {
-            **context,
-            'submission_type_list': get_annotated_feedback_count(),
-            'tutor_list':           get_annotated_tutor_list(),
-        }
-
-        return context
diff --git a/core/views/user_startpages.py b/core/views/user_startpages.py
index f6fd29f16b8398882e791c24200b08170bca4b7b..748c3da10c7a41a96b3fb030469c42a678abc7e6 100644
--- a/core/views/user_startpages.py
+++ b/core/views/user_startpages.py
@@ -1,87 +1,67 @@
 from django.contrib.auth.decorators import login_required
 from django.contrib.auth.models import User
-from django.db.models import Count, Q, F, Case, When, IntegerField, Value
 from django.http import HttpResponseRedirect
-from django.shortcuts import render
 from django.urls import reverse
 
-from core.custom_annotations import group_required, in_groups
-from core.models import Feedback, Submission, SubmissionType
+from core.custom_annotations import in_groups
+from core.models import Feedback, Student, SubmissionType
+from core.views.generics import (ReviewerListView, StudentDetailView,
+                                 TutorDetailView)
 
 
 @login_required(login_url='/')
 def user_home(request):
     if in_groups(request.user, ('Students', )):
-        return student_view(request)
+        return StudentStartPage.as_view()(request)
     elif in_groups(request.user, ('Tutors', )):
-        return tutor_view(request)
+        return TutorStartPage.as_view()(request)
     elif in_groups(request.user, ('Reviewers', )):
-        return reviewer_view(request)
+        return ReviewerFeedbackListView.as_view()(request)
     else:
         return HttpResponseRedirect(reverse('index'))
 
-def get_annotated_feedback_count():
-    """ Annotates submission lists with counts
-
-    count both
-        * number of submission per submission type
-        * count of received feedback per submission type
-        *
-    Alternative with case
-        Count(Case(
-            When(submissions__feedback_list__origin=Feedback.MANUAL,
-                then=Value(1)), output_field=IntegerField())
-        )
-
-    Returns:
-        annotated queryset
-    """
-    return SubmissionType.objects\
-        .annotate( # to display only manual
-            feedback_count=Count(
-                Case(
-                    When(Q(submissions__feedback__isnull=False) & Q(submissions__feedback__status=Feedback.ACCEPTED),
-                        then=Value(1)), output_field=IntegerField(),
-                )
-            )
-        ).annotate(
-            submission_count=Count('submissions')
-        ).annotate(
-            percentage=(F('feedback_count') * 100 / F('submission_count'))
-        ).all().order_by('name')
-
-def get_annotated_tutor_list():
-    return User.objects.annotate(Count('corrected_submissions')).filter(groups__name='Tutors') \
-                                                                .order_by('-corrected_submissions__count')
-
-@group_required('Tutors')
-def tutor_view(request):
-    submission_type = get_annotated_feedback_count()
-    context = {
-        'submission_type_list': submission_type,
-        'feedback_list': Feedback.objects.filter(of_tutor=request.user),
-        'feedback_open_list': Feedback.objects.filter(Q(status=Feedback.OPEN) & ~Q(of_tutor=request.user)),
-    }
-    return render(request, 'core/t/tutor_startpage.html', context)
-
-
-@group_required('Students')
-def student_view(request):
-    context = {
-        'student': request.user,
-        'submission_list': Submission.objects.filter(student__user=request.user)
-    }
-    return render(request, 'core/s/student_startpage.html', context)
-
-
-@group_required('Reviewers')
-def reviewer_view(request):
-    context = {
-        'submission_type_list': get_annotated_feedback_count(),
-        'tutor_list': get_annotated_tutor_list(),
-        'feedback_list_manual': Feedback.objects.filter(origin=Feedback.MANUAL),
-        'feedback_list_empty': Feedback.objects.filter(origin=Feedback.WAS_EMPTY),
-        'feedback_list_did_not_compile': Feedback.objects.filter(origin=Feedback.DID_NOT_COMPILE),
-        'feedback_list_could_not_link': Feedback.objects.filter(origin=Feedback.COULD_NOT_LINK),
-    }
-    return render(request, 'core/r/reviewer_startpage.html', context)
+
+class TutorStartPage(TutorDetailView):
+
+    model               = User
+    template_name       = 'core/t/tutor_startpage.html'
+    context_object_name = 'tutor'
+
+    def get_object(self):
+        return self.request.user
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+
+        return {
+            'submission_type_list': SubmissionType.get_annotated_feedback_count(),
+            'feedback_open_list': Feedback.get_open_feedback(self.get_object()),
+            **context
+        }
+
+
+class StudentStartPage(StudentDetailView):
+
+    model               = Student
+    template_name       = 'core/s/student_startpage.html'
+
+    def get_object(self):
+        return self.request.user.student
+
+
+class ReviewerFeedbackListView(ReviewerListView):
+    """ This is the de facto startpage of the reviewer accounts"""
+
+    model         = Feedback
+    template_name = 'core/r/reviewer_startpage.html'
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+
+        return {
+            'feedback_list_manual': self.model.objects.filter(origin=Feedback.MANUAL),
+            'feedback_list_empty': self.model.objects.filter(origin=Feedback.WAS_EMPTY),
+            'feedback_list_did_not_compile': self.model.objects.filter(origin=Feedback.DID_NOT_COMPILE),
+            'feedback_list_could_not_link': self.model.objects.filter(origin=Feedback.COULD_NOT_LINK),
+            **context
+        }
diff --git a/grady/settings/default.py b/grady/settings/default.py
index 646bf784389530c20921834f7fe5dc87756e2e4e..cdd76ebdd3bafa7746c0e9c36d25b131fdaf57fb 100644
--- a/grady/settings/default.py
+++ b/grady/settings/default.py
@@ -123,6 +123,9 @@ GRAPH_MODELS = {
     'group_models': True,
 }
 
+LOGIN_REDIRECT_URL  = '/'
+LOGIN_URL           = '/'
+
 
 MESSAGE_TAGS = {
     messages.DEBUG: 'alert-info',
diff --git a/util/importer.py b/util/importer.py
index 00d296226fabef031184d43c84c31c93b67325a2..b1598a059e4546e786c2bb4d842c01eb8ea7cebf 100644
--- a/util/importer.py
+++ b/util/importer.py
@@ -1,16 +1,17 @@
 import collections
 import csv
+import json
 import os
 import readline
 import secrets
-import json
 from typing import Callable
 
 from django.contrib.auth.models import Group, User
 
 import util.convert
 import util.processing
-from core.models import Feedback, Student, Submission, SubmissionType, Test, ExamType
+from core.models import (ExamType, Feedback, Student, Submission,
+                         SubmissionType, Test)
 from util.messages import *
 from util.processing import EmptyTest
 
diff --git a/util/populatedb.py b/util/populatedb.py
deleted file mode 100644
index f5a712b0d12d68a978a2e5b7765b616311d3f8c5..0000000000000000000000000000000000000000
--- a/util/populatedb.py
+++ /dev/null
@@ -1,297 +0,0 @@
-import argparse
-import csv
-import json
-import os
-from collections import namedtuple
-
-import django
-import xkcdpass.xkcd_password as xp
-from django.contrib.auth.models import Group, User
-
-from core.models import Feedback, Student, Submission, SubmissionType
-
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'grady.settings')
-
-django.setup()
-
-INFO = 1
-
-HTML_DIR = 'html'
-SOLUTION_DIR = 'code/code-lsg'
-
-wordfile = xp.locate_wordfile()
-wordlist = xp.generate_wordlist(wordfile=wordfile, min_length=5, max_length=8)
-
-
-
-
-if INFO:
-    info = print
-else:
-    info = lambda _: None
-
-
-def parseme():
-    parser = argparse.ArgumentParser()
-    parser.add_argument(
-        '--create-users',
-        help='Will just populate auth_db',
-        action='store_true')
-    parser.add_argument(
-        '--compiler-output-only',
-        help='Will only add compiler output to existing data',
-        action='store_true')
-    parser.add_argument(
-        'DATADIR',
-        help='a folder containing a predefined set of files with information',
-        default='data')
-    parser.add_argument(
-        '-s', '--submissions',
-        help='A file with submission code and student user names',
-        default='submissions.json',
-        metavar='FILE')
-    parser.add_argument(
-        '-t', '--tutors',
-        help='A list of tutor names',
-        default='tutors',
-        metavar="FILE")
-    parser.add_argument(
-        '-r', '--reviewers',
-        help='A list of reviewer names',
-        default='reviewers',
-        metavar="FILE")
-    parser.add_argument(
-        '-st', '--submission_types',
-        help='some kind of descriptions for all the submission types',
-        default='submission_types.csv',
-        metavar="FILE")
-
-    args = parser.parse_args()
-
-    args.tutors             = os.path.join(args.DATADIR, args.tutors)
-    args.reviewers          = os.path.join(args.DATADIR, args.reviewers)
-    args.submissions        = os.path.join(args.DATADIR, args.submissions)
-    args.submission_types   = os.path.join(args.DATADIR, args.submission_types)
-
-    return args
-
-
-def add_submission_type(name,
-                        score,
-                        task_description="__task_description: what the student saw",
-                        possible_solution="__possible_solution: a sample solution",
-                        correction_guideline="__possible_solution: a way to correct the task",):
-    task, created = SubmissionType.objects.get_or_create(name=name)
-    task.full_score = int(score)
-    task.task_description = task_description
-    task.possible_solution = possible_solution
-    task.correction_guideline = correction_guideline
-    task.save()
-    if created:
-        info(f"- Created Task {task.name}")
-    else:
-        info(f"- Task {task.name} was already created")
-    return task
-
-
-def student_has_all_submissions(student):
-    return Submission.objects.filter(student=student).count() \
-        == SubmissionType.objects.all().count()
-
-
-def add_submission(type, text, student, compiler_output):
-    if student_has_all_submissions(student):
-        return None
-
-    sub = Submission()
-    sub.type = type
-    sub.text = text
-    sub.student = student
-    sub.pre_corrections = compiler_output
-    sub.save()
-    add_auto_feedback(sub, compiler_output)
-    info(f"- Created Submission of Type {sub.type}")
-    return sub
-
-
-def add_compiler_output_only(type, text, student, compiler_output):
-
-    sub = Submission.objects.get(type=type, student=student)
-    if not sub:
-        return
-    sub.pre_corrections = compiler_output
-    sub.save()
-    info(f"- Added compiler output to submission {sub.type}")
-    return sub
-
-
-def add_auto_feedback(submission, compiler_output):
-    if submission.text and not compiler_output:
-        return # let the tutor do his job
-
-    def deduct_feedback_type() -> (str, str):
-        if not submission.text:
-            return Feedback.WAS_EMPTY, Feedback.ACCEPTED
-        elif compiler_output.endswith('DID NOT COMPILE'):
-            return Feedback.DID_NOT_COMPILE, Feedback.NEEDS_REVIEW
-        elif compiler_output.endswith('COULD NOT LINK'):
-            return Feedback.COULD_NOT_LINK, Feedback.NEEDS_REVIEW
-        return None, None
-
-    auto_correct, _ = User.objects.get_or_create(username='auto_correct')
-    feedback = Feedback()
-    feedback.text = "--- Was generated automatically ---"
-    feedback.origin, feedback.status = deduct_feedback_type()
-    if feedback.origin is None and feedback.status is None:
-        return
-    feedback.of_submission = submission
-    feedback.of_tutor = auto_correct
-    feedback.save()
-    if feedback.origin == Feedback.WAS_EMPTY:
-        submission.final_feedback = feedback
-        submission.save()
-    info(f"- Created {feedback.origin} Feedback for Submission {submission}")
-    return feedback
-
-
-def add_student(username, name, matrikel_no):
-    student_group, _ = Group.objects.get_or_create(name='Students')
-    student, created = Student.objects.get_or_create(
-        matrikel_no=matrikel_no,
-        user=add_user(username, student_group)
-    )
-    if created:
-        student.name = name
-        student.matrikel_no = matrikel_no
-        student.save()
-    return student
-
-
-def add_user(username, group):
-    user, created = User.objects.get_or_create(username=username)
-
-    if created:
-        password = xp.generate_xkcdpassword(wordlist, numwords=2)
-        login_writer.writerow([username, password])
-        user.set_password(password)
-        group.user_set.add(user)
-        info(f"- Created user {user} and added him to group {group}")
-        user.save()
-    else:
-        info(f"- User {user} of group {group} was already created.")
-
-    return user
-
-
-def add_group(group_name):
-    group, _ = Group.objects.get_or_create(name=group_name)
-    info(f"- Created group {group}")
-    return group
-
-
-def create_superuser():
-    try:
-        username = 'doncamillo'
-        password = xp.generate_xkcdpassword(wordlist, numwords=2)
-        login_writer.writerow(username, password)
-        User.objects.create_superuser(
-            username=username, password=password, email='mail-gardy@jmx.io')
-    except Exception as e:
-        info("- Superuser was already created.")
-        return
-
-
-class PopulateDatabase:
-
-    """docstring for PopulateDatabase"""
-
-    __slots__ = (
-        'args',
-        'type_dict',
-        'student_group',
-        'tutor_group',
-        'reviewer_group',
-    )
-
-    def __init__(self, args):
-        self.args = args
-        if self.args.create_users:
-            self.create_groups()
-            self.create_user_accounts()
-        else: # dirty
-            self.create_submission_types()
-            self.populate_submissions()
-
-    def create_groups(self):
-        self.student_group  = add_group('Students')
-        self.tutor_group    = add_group('Tutors')
-        self.reviewer_group = add_group('Reviewers')
-
-    def create_user_accounts(self):
-        with open(self.args.tutors) as tutors:
-            for tutor in tutors:
-                add_user(tutor.strip(), self.tutor_group)
-
-        with open(self.args.reviewers) as reviewers:
-            for reviewer in reviewers:
-                add_user(reviewer.strip(), self.reviewer_group)
-
-    def create_submission_types(self):
-        submission_type = namedtuple('submission_type', 'id name score')
-        with open(args.submission_types) as data:
-            types = list(submission_type(*line.strip().split(', '))
-                         for line in data if line)
-
-        self.type_dict = {}
-        for t in types:
-            with \
-                    open(os.path.join(self.args.DATADIR, SOLUTION_DIR, t.id + '-lsg.c' )) as lsg, \
-                    open(os.path.join(self.args.DATADIR, HTML_DIR, t.id + '.html' )) as desc:
-                self.type_dict[t.id] = add_submission_type(
-                    f"[{t.id}] {t.name}",
-                    t.score,
-                    desc.read(),
-                    lsg.read(),
-                )
-
-    def populate_submissions(self):
-        with open(self.args.submissions) as data:
-            stud_data = json.JSONDecoder().decode(data.read())
-
-        for user, userdata in stud_data.items():
-            student = add_student(
-                user, userdata['name'], userdata['matrikel_no'])
-            for s, code in userdata['submissions'].items():
-                if self.args.compiler_output_only:
-                    add_compiler_output_only(
-                        self.type_dict[s], code, student,
-                        userdata['compiler_output'][s]
-                    )
-                else:
-                    add_submission(
-                        self.type_dict[s], code, student,
-                        userdata['compiler_output'][s]
-                    )
-
-
-# Start execution here!
-if __name__ == '__main__':
-    args = parseme()
-    print("Starting population script...")
-
-    LOGIN_FILE = 'login_data.csv'
-
-    try:
-        login_data_f = open(LOGIN_FILE, 'a')
-        if os.stat(LOGIN_FILE).st_size == 0:
-            login_writer = csv.writer(login_data_f)
-            login_writer.writerow(['username', 'password'])
-        else:
-            login_writer = csv.writer(login_data_f)
-
-        # start the actual population
-        create_superuser()
-        PopulateDatabase(args)
-
-    finally:
-        login_data_f.close()
diff --git a/util/testcases.py b/util/testcases.py
index 3e9892fdf4c07a4babf315e1a9437fac7e7c5899..491d3074bb9261c786cc8915ef2f7f950a78e861 100644
--- a/util/testcases.py
+++ b/util/testcases.py
@@ -3,6 +3,7 @@ import os
 import random
 import re
 from string import ascii_letters, digits
+
 try:
     import processing
 except ModuleNotFoundError: