diff --git a/core/models.py b/core/models.py
index 7acdf52b622ef530bf837a87c1b305c3f8cc0565..80c3a42fde446ee8022779ccca3695c42b868c7f 100644
--- a/core/models.py
+++ b/core/models.py
@@ -180,11 +180,6 @@ class UserAccount(AbstractUser):
     def is_reviewer(self):
         return self.role == 'Reviewer'
 
-    def done_assignments_count(self) -> int:
-        # TODO optimize this query
-        return sum([subscription.assignments.filter(done=True).count()
-                    for subscription in self.subscriptions.all()])
-
     @classmethod
     def get_students(cls):
         return cls.objects.filter(role=cls.STUDENT)
diff --git a/core/serializers/common_serializers.py b/core/serializers/common_serializers.py
index 72c0a437d1871efccb5ff5f6987f1529c00748e9..5729947a4da711ed6964f78268144b5fab4a2170 100644
--- a/core/serializers/common_serializers.py
+++ b/core/serializers/common_serializers.py
@@ -2,7 +2,7 @@ import logging
 
 from rest_framework import serializers
 
-from core.models import ExamType, SubmissionType, Test, UserAccount
+from core import models
 from util.factories import GradyUserFactory
 
 from .generic import DynamicFieldsModelSerializer
@@ -14,7 +14,7 @@ user_factory = GradyUserFactory()
 class ExamSerializer(DynamicFieldsModelSerializer):
 
     class Meta:
-        model = ExamType
+        model = models.ExamType
         fields = ('pk', 'module_reference', 'total_score',
                   'pass_score', 'pass_only',)
 
@@ -22,33 +22,50 @@ class ExamSerializer(DynamicFieldsModelSerializer):
 class TestSerializer(DynamicFieldsModelSerializer):
 
     class Meta:
-        model = Test
+        model = models.Test
         fields = ('pk', 'name', 'label', 'annotation')
 
 
 class SubmissionTypeListSerializer(DynamicFieldsModelSerializer):
 
     class Meta:
-        model = SubmissionType
+        model = models.SubmissionType
         fields = ('pk', 'name', 'full_score')
 
 
 class SubmissionTypeSerializer(SubmissionTypeListSerializer):
 
     class Meta:
-        model = SubmissionType
+        model = models.SubmissionType
         fields = ('pk', 'name', 'full_score', 'description', 'solution')
 
 
 class TutorSerializer(DynamicFieldsModelSerializer):
-    done_assignments_count = serializers.IntegerField(
-        read_only=True)
-
-    def create(self, validated_data) -> UserAccount:
+    feedback_created = serializers.SerializerMethodField()
+    feedback_validated = serializers.SerializerMethodField()
+
+    @staticmethod
+    def _get_completed_assignments(obj):
+        return models.TutorSubmissionAssignment.objects.filter(
+            is_done=True,
+            subscription__owner=obj,
+        )
+
+    def get_feedback_created(self, obj):
+        return self._get_completed_assignments(obj).filter(
+            subscription__feedback_stage=models.SubmissionSubscription.FEEDBACK_CREATION  # noqa
+        ).count()
+
+    def get_feedback_validated(self, obj):
+        return self._get_completed_assignments(obj).filter(
+            subscription__feedback_stage=models.SubmissionSubscription.FEEDBACK_VALIDATION  # noqa
+        ).count()
+
+    def create(self, validated_data) -> models.UserAccount:
         log.info("Crating tutor from data %s", validated_data)
         return user_factory.make_tutor(
             username=validated_data['username'])
 
     class Meta:
-        model = UserAccount
-        fields = ('pk', 'username', 'done_assignments_count')
+        model = models.UserAccount
+        fields = ('pk', 'username', 'feedback_created', 'feedback_validated')
diff --git a/core/serializers/submission.py b/core/serializers/submission.py
index f8d777e4029156aefe3d3b5ff3672d051c5adc19..2851460590661fa89a4928befbe98d824c71fde4 100644
--- a/core/serializers/submission.py
+++ b/core/serializers/submission.py
@@ -8,7 +8,6 @@ from core.serializers import (DynamicFieldsModelSerializer, FeedbackSerializer,
 
 class SubmissionNoTextFieldsSerializer(DynamicFieldsModelSerializer):
     score = serializers.ReadOnlyField(source='feedback.score')
-    type = serializers.ReadOnlyField(source='type.name')
     full_score = serializers.ReadOnlyField(source='type.full_score')
 
     class Meta:
@@ -26,9 +25,18 @@ class SubmissionSerializer(DynamicFieldsModelSerializer):
         fields = ('pk', 'type', 'text', 'feedback', 'tests')
 
 
+class SubmissionNoTypeSerializer(DynamicFieldsModelSerializer):
+    feedback = FeedbackSerializer()
+    full_score = serializers.ReadOnlyField(source='type.full_score')
+    tests = TestSerializer(many=True)
+
+    class Meta:
+        model = Submission
+        fields = ('pk', 'type', 'full_score', 'text', 'feedback', 'tests')
+
+
 class SubmissionListSerializer(DynamicFieldsModelSerializer):
     type = SubmissionTypeListSerializer(fields=('pk', 'name', 'full_score'))
-    # TODO change this according to new feedback model
     feedback = FeedbackSerializer()
 
     class Meta:
diff --git a/core/serializers/subscription.py b/core/serializers/subscription.py
index ea87cb07371b96835cf1a91dbb904bfea1bc4e96..90ae0e2ef6d4d95b4f04a13a3e0f8383c54f7c40 100644
--- a/core/serializers/subscription.py
+++ b/core/serializers/subscription.py
@@ -31,9 +31,6 @@ class AssignmentDetailSerializer(AssignmentSerializer):
         model = TutorSubmissionAssignment
         fields = ('pk', 'submission', 'feedback', 'is_done', 'subscription')
         read_only_fields = ('is_done', 'submission', 'feedback')
-        extra_kwargs = {
-            'subscription': {'write_only': True},
-        }
 
     def create(self, validated_data):
         subscription = validated_data.get('subscription')
@@ -43,10 +40,18 @@ class AssignmentDetailSerializer(AssignmentSerializer):
 class SubscriptionSerializer(DynamicFieldsModelSerializer):
     owner = serializers.ReadOnlyField(source='owner.username')
     query_key = serializers.UUIDField(required=False)
-    assignments = AssignmentSerializer(read_only=True, many=True)
+    assignments = serializers.SerializerMethodField()
     remaining = serializers.SerializerMethodField()
     available = serializers.SerializerMethodField()
 
+    def get_assignments(self, subscription):
+        queryset = TutorSubmissionAssignment.objects.filter(
+            subscription=subscription,
+            is_done=False
+        )
+        serializer = AssignmentDetailSerializer(queryset, many=True)
+        return serializer.data
+
     def get_remaining(self, subscription):
         return subscription.get_remaining_not_final()
 
diff --git a/core/tests/test_auth.py b/core/tests/test_auth.py
index d9af01e801c52cd9de3e061c31ea3588252b306e..e517ccf073d5832d3a81491ee4ec7dfe2e998f1d 100644
--- a/core/tests/test_auth.py
+++ b/core/tests/test_auth.py
@@ -15,10 +15,10 @@ class AuthTests(APITestCase):
         cls.client = APIClient()
 
     def test_get_token(self):
-        response = self.client.post('/api-token-auth/', self.credentials)
+        response = self.client.post('/api/get-token/', self.credentials)
         self.assertContains(response, 'token')
 
     def test_refresh_token(self):
-        token = self.client.post('/api-token-auth/', self.credentials).data
-        response = self.client.post('/api-token-refresh/', token)
+        token = self.client.post('/api/get-token/', self.credentials).data
+        response = self.client.post('/api/refresh-token/', token)
         self.assertContains(response, 'token')
diff --git a/core/tests/test_tutor_api_endpoints.py b/core/tests/test_tutor_api_endpoints.py
index adbed0b9001e95f016cbcf3706d9c53e019955f6..0e9a5773a8785dbcf67de3fad2dcb4625ef6ec18 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 TutorSubmissionAssignment
+from core.models import TutorSubmissionAssignment, SubmissionSubscription
 from core.views import TutorApiViewSet
 from util.factories import GradyUserFactory
 
@@ -66,17 +66,33 @@ class TutorListTests(APITestCase):
     def test_get_a_list_of_all_tutors(self):
         self.assertEqual(len(self.response.data), NUMBER_OF_TUTORS)
 
-    def test_feedback_count_matches_database(self):
+    def test_feedback_created_count_matches_database(self):
         def verify_fields(tutor_obj):
             t = get_user_model().objects.get(username=tutor_obj['username'])
-            return t.done_assignments_count() == \
-                tutor_obj['done_assignments_count']
+            feedback_created_count = TutorSubmissionAssignment.objects.filter(
+                is_done=True,
+                subscription__feedback_stage=SubmissionSubscription.FEEDBACK_CREATION,  # noqa
+                subscription__owner=t
+            ).count()
+            return feedback_created_count == tutor_obj['feedback_created']
+
+        self.assertTrue(all(map(verify_fields, self.response.data)))
+
+    def test_feedback_validated_count_matches_database(self):
+        def verify_fields(tutor_obj):
+            t = get_user_model().objects.get(username=tutor_obj['username'])
+            feedback_validated_cnt = TutorSubmissionAssignment.objects.filter(
+                is_done=True,
+                subscription__feedback_stage=SubmissionSubscription.FEEDBACK_VALIDATION,  # noqa
+                subscription__owner=t
+            ).count()
+            return feedback_validated_cnt == tutor_obj['feedback_created']
 
         self.assertTrue(all(map(verify_fields, self.response.data)))
 
     def test_sum_of_done_assignments(self):
         self.assertEqual(
-            sum(obj['done_assignments_count']
+            sum(obj['feedback_created'] + obj['feedback_validated']
                 for obj in self.response.data),
             TutorSubmissionAssignment.objects.filter(is_done=True).count()
         )
diff --git a/core/urls.py b/core/urls.py
index 4853a567b10041c67865b05753a145482a6350c9..6b37ef23531fce77a2fc49c0b28d1c202826991a 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -14,6 +14,7 @@ router.register('tutor', views.TutorApiViewSet, base_name='tutor')
 router.register('subscription', views.SubscriptionApiViewSet,
                 base_name='subscription')
 router.register('assignment', views.AssignmentApiViewSet)
+router.register('submission', views.SubmissionViewSet)
 
 # regular views that are not viewsets
 regular_views_urlpatterns = [
diff --git a/core/views/common_views.py b/core/views/common_views.py
index 344db1a285598053c5216d8d8d6b672f83b82de4..b79dec5c6eaeefc8cde76beb1a7613d4471fc385 100644
--- a/core/views/common_views.py
+++ b/core/views/common_views.py
@@ -14,7 +14,7 @@ from core.permissions import IsReviewer, IsStudent
 from core.serializers import (ExamSerializer, StudentInfoSerializer,
                               StudentInfoSerializerForListView,
                               SubmissionSerializer, SubmissionTypeSerializer,
-                              TutorSerializer)
+                              TutorSerializer, SubmissionNoTypeSerializer)
 
 log = logging.getLogger(__name__)
 
@@ -82,3 +82,9 @@ class SubmissionTypeApiView(viewsets.ReadOnlyModelViewSet):
     """ Gets a list or a detail view of a single SubmissionType """
     queryset = SubmissionType.objects.all()
     serializer_class = SubmissionTypeSerializer
+
+
+class SubmissionViewSet(viewsets.ReadOnlyModelViewSet):
+    permission_classes = (IsReviewer, )
+    queryset = models.Submission.objects.all()
+    serializer_class = SubmissionNoTypeSerializer
diff --git a/core/views/subscription.py b/core/views/subscription.py
index d806747f9c30ebdde03e2ee5170d392dad06f0d5..2e00c5cac40596eeb3359b42dc159e70639e90bf 100644
--- a/core/views/subscription.py
+++ b/core/views/subscription.py
@@ -23,7 +23,9 @@ class SubscriptionApiViewSet(
 
     def get_queryset(self):
         return models.SubmissionSubscription.objects.filter(
-            owner=self.request.user)
+            owner=self.request.user,
+            deactivated=False
+        )
 
     def _get_subscription_if_type_exists(self, data):
         try:
@@ -92,7 +94,7 @@ class AssignmentApiViewSet(
         """ Stop working on the assignment before it is finished """
         instance = self.get_object()
 
-        if instance.is_done:
+        if instance.is_done or instance.subscription.owner != request.user:
             return Response(status=status.HTTP_403_FORBIDDEN)
 
         instance.delete()
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index 681f5c0a671aeb44b8365fb992d746e5050be24d..01a48583c61d1a3668596ca78a162e388c639271 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -48,7 +48,7 @@
         'lastAppInteraction'
       ]),
       ...mapState({
-        tokenCreationTime: state => state.authentication.tokenCreationTime,
+        lastTokenRefreshTry: state => state.authentication.lastTokenRefreshTry,
         refreshingToken: state => state.authentication.refreshingToken,
         jwtTimeDelta: state => state.authentication.jwtTimeDelta
       })
@@ -65,10 +65,10 @@
     },
     watch: {
       lastAppInteraction: function (val) {
-        const timeSinceLastRefresh = Date.now() - this.tokenCreationTime
+        const timeSinceLastRefresh = Date.now() - this.lastTokenRefreshTry
         const timeDelta = this.jwtTimeDelta
         // refresh jwt if it's older than 20% of his maximum age
-        if (timeDelta > 0 && timeSinceLastRefresh > timeDelta * 0.2 &&
+        if (this.$route.name !== 'login' && timeSinceLastRefresh > timeDelta * 0.2 &&
           !this.refreshingToken) {
           this.$store.dispatch('refreshJWT')
         }
diff --git a/frontend/src/api.js b/frontend/src/api.js
index 007ebc14f9879211e782a5c0d6d03d209c8b48b5..292afb200428eb9eb41dace3a180a6b1a16d40dc 100644
--- a/frontend/src/api.js
+++ b/frontend/src/api.js
@@ -20,13 +20,13 @@ let ax = axios.create({
 })
 
 export async function fetchJWT (credentials) {
-  const token = (await ax.post('/api-token-auth/', credentials)).data.token
+  const token = (await ax.post('/api/get-token/', credentials)).data.token
   ax.defaults.headers['Authorization'] = `JWT ${token}`
   return token
 }
 
 export async function refreshJWT (token) {
-  const newToken = (await ax.post('/api-token-refresh/', {token})).data.token
+  const newToken = (await ax.post('/api/refresh-token/', {token})).data.token
   ax.defaults.headers['Authorization'] = `JWT ${newToken}`
   return token
 }
@@ -47,12 +47,37 @@ export async function fetchStudentSubmissions () {
   return (await ax.get('/api/student-submissions/')).data
 }
 
+export async function fetchSubmissionFeedbackTests ({pk}) {
+  return (await ax.get(`/api/submission/${pk}`)).data
+}
+
+export async function fetchAllStudents (fields = []) {
+  const url = addFieldsToUrl({
+    url: '/api/student/',
+    fields
+  })
+  return (await ax.get(url)).data
+}
+
+export async function fetchAllTutors (fields = []) {
+  const url = addFieldsToUrl({
+    url: '/api/tutor/',
+    fields
+  })
+  return (await ax.get(url)).data
+}
+
 export async function fetchSubscriptions () {
   return (await ax.get('/api/subscription/')).data
 }
 
+export async function deactivateSubscription ({pk}) {
+  const url = `/api/subscription/${pk}/`
+  return (await ax.delete(url)).data
+}
+
 export async function fetchSubscription (subscriptionPk) {
-  return (await ax.get(`/api/subscription/${subscriptionPk}`)).data
+  return (await ax.get(`/api/subscription/${subscriptionPk}/`)).data
 }
 
 export async function fetchExamType ({examPk, fields = []}) {
@@ -77,21 +102,19 @@ export async function subscribeTo (type, key, stage) {
   return (await ax.post('/api/subscription/', data)).data
 }
 
-export async function fetchCurrentAssignment (subscriptionPk) {
-  return (await ax.get(`/api/subscription/${subscriptionPk}/assignments/current/`)).data
+export async function createAssignment ({subscription}) {
+  const data = {
+    subscription: subscription.pk
+  }
+  return (await ax.post(`/api/assignment/`, data)).data
 }
 
-export async function fetchNextAssignment (subscriptionPk) {
-  return (await ax.get(`/api/subscription/${subscriptionPk}/assignments/next/`)).data
+export async function submitFeedbackForAssignment ({feedback}) {
+  return (await ax.post('/api/feedback/', feedback)).data
 }
 
-export async function submitFeedbackForAssignment (feedback, assignmentPk) {
-  const data = {
-    ...feedback,
-    assignment_pk: assignmentPk
-  }
-
-  return (await ax.post('/api/feedback/', data)).data
+export async function submitUpdatedFeedback ({feedback}) {
+  return (await ax.patch(`/api/feedback/${feedback.of_submission}/`, feedback)).data
 }
 
 export async function fetchSubmissionTypes (fields = []) {
@@ -102,4 +125,9 @@ export async function fetchSubmissionTypes (fields = []) {
   return (await ax.get(url)).data
 }
 
+export async function deleteAssignment ({assignment}) {
+  const url = `/api/assignment/${assignment.pk}`
+  return (await ax.delete(url)).data
+}
+
 export default ax
diff --git a/frontend/src/components/student_list/StudentList.vue b/frontend/src/components/student_list/StudentList.vue
new file mode 100644
index 0000000000000000000000000000000000000000..28f9b13b4b042de41307442929d9dd179a1ea1f7
--- /dev/null
+++ b/frontend/src/components/student_list/StudentList.vue
@@ -0,0 +1,174 @@
+<template>
+  <v-card>
+    <v-card-title>
+      <span class="title">
+        Students
+      </span>
+      <v-spacer/>
+      <v-text-field
+        append-icon="search"
+        label="Search"
+        single-line
+        hide-details
+        v-model="search"
+      ></v-text-field>
+    </v-card-title>
+    <v-data-table
+      :headers="dynamicHeaders"
+      :items="studentListItems"
+      :search="search"
+      :pagination.sync="pagination"
+      item-key="name"
+      hide-actions
+    >
+      <template slot="headers" slot-scope="props">
+        <tr>
+          <th
+            v-for="header in props.headers" :key="header.text"
+            :class="['column sortable', pagination.descending ? 'desc' : 'asc', header.value === pagination.sortBy ? 'active' : '']"
+            style="padding: 0;"
+            @click="changeSort(header.value)"
+          >
+            <v-icon>arrow_upward</v-icon>
+            {{ header.text }}
+          </th>
+        </tr>
+      </template>
+      <template slot="items" slot-scope="props">
+        <tr>
+          <td>
+            <v-btn small icon @click="props.expanded = !props.expanded">
+              <v-icon v-if="props.expanded">keyboard_arrow_up</v-icon>
+              <v-icon v-else>keyboard_arrow_down</v-icon>
+            </v-btn>
+            {{props.item.name}}
+          </td>
+          <td
+            v-for="type in submissionTypeHeaders"
+            style="padding: 0"
+            :key="type.name"
+            class="text-xs-right"
+          >
+            <v-btn
+              small round outline class="submission-button"
+              exact
+              :to="{name: 'submission-side-view', params: {pk: props.item[type.pk].pk}}"
+            >
+              {{props.item[type.pk].score}}
+            </v-btn>
+          </td>
+          <td
+            style="padding: 0 15px;"
+            class="text-xs-right"
+          >{{props.item.total}}</td>
+        </tr>
+      </template>
+      <template slot="expand" slot-scope="props">
+        <v-card flat>
+          <v-card-text>
+            Modul: {{props.item.exam}}
+          </v-card-text>
+        </v-card>
+      </template>
+    </v-data-table>
+  </v-card>
+</template>
+
+<script>
+  import {mapActions, mapState} from 'vuex'
+
+  export default {
+    name: 'student-list',
+    data () {
+      return {
+        search: '',
+        pagination: {
+          sortBy: 'name',
+          rowsPerPage: Infinity
+        },
+        staticHeaders: [
+          {
+            text: 'Name',
+            align: 'left',
+            value: 'name'
+          }
+        ]
+      }
+    },
+    computed: {
+      ...mapState([
+        'students'
+      ]),
+      submissionTypeHeaders () {
+        const subTypes = Object.values(this.$store.state.submissionTypes)
+        return subTypes.map(type => {
+          return {
+            pk: type.pk,
+            text: type.name.substr(0, 5),
+            value: `${type.pk}.score`,
+            align: 'right'
+          }
+        })
+      },
+      dynamicHeaders () {
+        const totalScoreHeader = {
+          text: 'Total',
+          align: 'right',
+          value: 'total'
+        }
+        let headers = this.staticHeaders.concat(this.submissionTypeHeaders)
+        headers.push(totalScoreHeader)
+        return headers
+      },
+      studentListItems () {
+        return this.students.map(student => {
+          return {
+            pk: student.pk,
+            user: student.user,
+            exam: student.exam,
+            name: student.name,
+            ...this.reduceArrToDict(student.submissions, 'type'),
+            total: this.sumSubmissionScores(student.submissions)
+          }
+        })
+      }
+    },
+    methods: {
+      ...mapActions([
+        'getStudents'
+      ]),
+      reduceArrToDict (arr, key) {
+        return arr.reduce((acc, curr) => {
+          const keyInDict = curr[key]
+          acc[keyInDict] = curr
+          return acc
+        }, {})
+      },
+      sumSubmissionScores (submissions) {
+        return submissions.reduce((acc, curr) => {
+          if (curr.score) {
+            acc += curr.score
+          }
+          return acc
+        }, 0)
+      },
+      changeSort (column) {
+        if (this.pagination.sortBy === column) {
+          this.pagination.descending = !this.pagination.descending
+        } else {
+          this.pagination.sortBy = column
+          this.pagination.descending = false
+        }
+      }
+    },
+    created () {
+      this.getStudents()
+    }
+  }
+</script>
+
+<style scoped>
+  .submission-button {
+    min-width: 40px;
+  }
+</style>
diff --git a/frontend/src/components/student_list/StudentListHelpCard.vue b/frontend/src/components/student_list/StudentListHelpCard.vue
new file mode 100644
index 0000000000000000000000000000000000000000..517bfc0acb410d44cbe773c09f4e9800e81a25eb
--- /dev/null
+++ b/frontend/src/components/student_list/StudentListHelpCard.vue
@@ -0,0 +1,29 @@
+<template>
+  <v-layout justify-center>
+    <v-card class="mt-5">
+      <v-card-title class="title">
+        This is the student overview page!
+      </v-card-title>
+      <v-card-text>
+        To the left you see all students as well as their scores
+        per task type. You can do the following:<br><br>
+        <ol style="padding-left: 30px;">
+          <li>click the little arrow on the left to see additional student information (matrikel no., module, etc.)</li>
+          <li>click on a students score to see their submission including feedback, tests, etc.<br>(You can even create Feedback here!)</li>
+          <li>sort the table via clicking on the table headers</li>
+          <li>search for a student via the search bar</li>
+        </ol>
+      </v-card-text>
+    </v-card>
+  </v-layout>
+</template>
+
+<script>
+  export default {
+    name: 'student-list-help-card'
+  }
+</script>
+
+<style scoped>
+
+</style>
diff --git a/frontend/src/components/submission_notes/SubmissionCorrection.vue b/frontend/src/components/submission_notes/SubmissionCorrection.vue
index f2d1151c7744009e9e85e72b1352affa76ddf02a..54a18b04ca76549a284052a2d6ab10d756579914 100644
--- a/frontend/src/components/submission_notes/SubmissionCorrection.vue
+++ b/frontend/src/components/submission_notes/SubmissionCorrection.vue
@@ -45,7 +45,9 @@
         slot="footer"
         :loading="loading"
         :fullScore="submissionObj['full_score']"
+        :skippable="assignment !== undefined"
         @submitFeedback="submitFeedback"
+        @skip="$emit('skip')"
       />
     </base-annotated-submission>
   </v-container>
@@ -80,7 +82,6 @@
       assignment: {
         type: Object
       },
-
       // either pass in an assignment or a submission and feedback
       submissionWithoutAssignment: {
         type: Object
@@ -93,8 +94,8 @@
       ...mapState({
         showEditorOnLine: state => state.submissionNotes.ui.showEditorOnLine,
         selectedComment: state => state.submissionNotes.ui.selectedCommentOnLine,
-        origFeedback: state => state.submissionNotes.orig.feedbackLines,
-        updatedFeedback: state => state.submissionNotes.updated.feedbackLines
+        origFeedback: state => state.submissionNotes.origFeedback.feedback_lines,
+        updatedFeedback: state => state.submissionNotes.updatedFeedback.feedback_lines
       }),
       ...mapGetters([
         'isStudent',
@@ -121,9 +122,11 @@
       toggleEditorOnLine (lineNo, comment = '') {
         this.$store.commit(subNotesNamespace(subNotesMut.TOGGLE_EDITOR_ON_LINE), {lineNo, comment})
       },
-      submitFeedback () {
+      submitFeedback ({isFinal}) {
         this.loading = true
-        this.$store.dispatch(subNotesNamespace('submitFeedback'), this.assignment).then(() => {
+        this.$store.dispatch(subNotesNamespace('submitFeedback'), {
+          isFinal: isFinal
+        }).then(() => {
           this.$store.commit(subNotesNamespace(subNotesMut.RESET_STATE))
           this.$emit('feedbackCreated')
         }).catch(err => {
@@ -138,7 +141,7 @@
       },
       init () {
         this.$store.commit(subNotesNamespace(subNotesMut.RESET_STATE))
-        this.$store.commit(subNotesNamespace(subNotesMut.SET_RAW_SUBMISSION), this.submissionObj.text)
+        this.$store.commit(subNotesNamespace(subNotesMut.SET_SUBMISSION), this.submissionObj)
         this.$store.commit(subNotesNamespace(subNotesMut.SET_ORIG_FEEDBACK), this.feedbackObj)
       }
     },
@@ -148,6 +151,12 @@
         this.$nextTick(() => {
           window.PR.prettyPrint()
         })
+      },
+      submissionWithoutAssignment: function () {
+        this.init()
+        this.$nextTick(() => {
+          window.PR.prettyPrint()
+        })
       }
     },
     created () {
diff --git a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue
index f5b0e268ab3f7850f45209a4c8b519648da2497d..a723a5bc405a498c78d03836a3559e3529196318 100644
--- a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue
+++ b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue
@@ -1,5 +1,13 @@
 <template>
   <v-toolbar dense class="bottom-toolbar">
+    <v-tooltip top v-if="skippable">
+    <v-btn
+      slot="activator"
+      outline round color="grey darken-2"
+      @click="$emit('skip')"
+    >Skip</v-btn>
+      <span>Skip this submission</span>
+    </v-tooltip>
     <v-spacer/>
     <v-alert
       class="score-alert ma-3"
@@ -26,6 +34,13 @@
       @click="score = fullScore"
       color="blue darken-3"
       class="score-button">{{fullScore}}</v-btn>
+    <v-tooltip top v-if="showFinalCheckbox">
+      <v-toolbar-items slot="activator" style="margin-top: 28px;">
+        <v-checkbox slot="activator" v-model="isFinal" class="final-checkbox"/>
+        <span>Final</span>
+      </v-toolbar-items>
+      <span>Non final feedback will be sent to the reviewer.</span>
+    </v-tooltip>
     <v-tooltip top>
       <v-btn
         color="success"
@@ -45,7 +60,8 @@
     name: 'annotated-submission-bottom-toolbar',
     data () {
       return {
-        scoreError: ''
+        scoreError: '',
+        isFinal: !this.$store.state.submissionNotes.isFeedbackCreation || this.$store.getters.isReviewer
       }
     },
     props: {
@@ -56,6 +72,10 @@
       loading: {
         type: Boolean,
         required: true
+      },
+      skippable: {
+        type: Boolean,
+        default: false
       }
     },
     computed: {
@@ -66,6 +86,9 @@
         set: function (score) {
           this.$store.commit(subNotesNamespace(subNotesMut.UPDATE_FEEDBACK_SCORE), Number(score))
         }
+      },
+      showFinalCheckbox () {
+        return !this.$store.state.submissionNotes.isFeedbackCreation || this.$store.getters.isReviewer
       }
     },
     methods: {
@@ -86,7 +109,7 @@
         return false
       },
       submit () {
-        this.$emit('submitFeedback')
+        this.$emit('submitFeedback', {isFinal: this.isFinal})
       }
     }
   }
@@ -109,4 +132,7 @@
   .score-button {
     min-width: 0px;
   }
+  .final-checkbox {
+    float: left;
+  }
 </style>
diff --git a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionTopToolbar.vue b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionTopToolbar.vue
index fc0ed47213b594e229235a9dbf16cf88c260ce71..84b36e423a07ba2eab620c736b54fc1d752fcd92 100644
--- a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionTopToolbar.vue
+++ b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionTopToolbar.vue
@@ -38,7 +38,7 @@
     },
     computed: {
       ...mapState({
-        submission: state => state.submissionNotes.orig.rawSubmission
+        submission: state => state.submissionNotes.submission.text
       })
     },
     methods: {
diff --git a/frontend/src/components/subscriptions/Subscription.vue b/frontend/src/components/subscriptions/Subscription.vue
deleted file mode 100644
index dd396ca1750e546281163c23f21ab41e4a427c7a..0000000000000000000000000000000000000000
--- a/frontend/src/components/subscriptions/Subscription.vue
+++ /dev/null
@@ -1,45 +0,0 @@
-<template>
-  <v-list-tile
-    exact
-    :to="{name: 'subscription', params: {pk: subscriptionPk}}"
-  >
-    <v-list-tile-content class="ml-3">
-      {{query_key ? query_key : 'Active'}}
-    </v-list-tile-content>
-    <v-list-tile-action-text>
-      {{stageMap[feedback_stage]}}
-    </v-list-tile-action-text>
-  </v-list-tile>
-</template>
-
-<script>
-  export default {
-    name: 'subscription',
-    props: {
-      subscriptionPk: {
-        types: String,
-        required: true
-      },
-      feedback_stage: {
-        type: String,
-        required: true
-      },
-      query_key: {
-        type: String
-      }
-    },
-    data () {
-      return {
-        stageMap: {
-          'feedback-creation': 'create',
-          'feedback-validation': 'validate',
-          'feedback-conflict-resolution': 'conflict'
-        }
-      }
-    }
-  }
-</script>
-
-<style scoped>
-
-</style>
diff --git a/frontend/src/components/subscriptions/SubscriptionCreation.vue b/frontend/src/components/subscriptions/SubscriptionCreation.vue
index a9919b4fa2a405535adc9d288d318c4c88f82d90..7463a3d8203a70c94ece3f1ef3c49eb944d6044b 100644
--- a/frontend/src/components/subscriptions/SubscriptionCreation.vue
+++ b/frontend/src/components/subscriptions/SubscriptionCreation.vue
@@ -33,8 +33,14 @@
     name: 'subscription-creation',
     data () {
       return {
-        key: '',
-        stage: '',
+        key: {
+          text: '',
+          key: ''
+        },
+        stage: {
+          text: '',
+          stage: ''
+        },
         loading: false
       }
     },
@@ -56,17 +62,17 @@
         let stages = [
           {
             text: 'Initial Feedback',
-            type: 'feedback-creation'
+            stage: 'feedback-creation'
           },
           {
             text: 'Feedback validation',
-            type: 'feedback-validation'
+            stage: 'feedback-validation'
           }
         ]
         if (this.$store.getters.isReviewer) {
           stages.push({
             text: 'Conflict resolution',
-            type: 'feedback-conflict-resolution'
+            stage: 'feedback-conflict-resolution'
           })
         }
         return stages
@@ -77,8 +83,8 @@
         this.loading = true
         this.$store.dispatch('subscribeTo', {
           type: this.type,
-          key: this.key.text,
-          stage: this.stage.type
+          key: this.key.key,
+          stage: this.stage.stage
         }).catch(err => {
           if (err.response && err.response.data['non_field_errors']) {
             this.$notify({
diff --git a/frontend/src/components/subscriptions/SubscriptionForList.vue b/frontend/src/components/subscriptions/SubscriptionForList.vue
new file mode 100644
index 0000000000000000000000000000000000000000..c02e9275426fcd872893d00276c30a8f47506376
--- /dev/null
+++ b/frontend/src/components/subscriptions/SubscriptionForList.vue
@@ -0,0 +1,83 @@
+<template>
+  <v-layout row>
+    <v-list-tile
+      exact
+      :to="{name: 'subscription', params: {pk: subscriptionPk}}"
+      style="width: 100%"
+    >
+      <v-list-tile-content class="ml-3">
+        {{name}}
+      </v-list-tile-content>
+      <v-list-tile-action-text>
+        {{stageMap[feedback_stage]}}
+      </v-list-tile-action-text>
+    </v-list-tile>
+    <v-btn
+      icon
+      @click="deactivate"
+    >
+      <v-icon
+        color="grey"
+        style="font-size: 20px"
+      >
+        delete
+      </v-icon>
+    </v-btn>
+  </v-layout>
+</template>
+
+<script>
+  export default {
+    name: 'subscription-for-list',
+    props: {
+      subscriptionPk: {
+        types: String,
+        required: true
+      },
+      feedback_stage: {
+        type: String,
+        required: true
+      },
+      query_type: {
+        type: String,
+        required: true
+      },
+      query_key: {
+        type: String
+      }
+    },
+    data () {
+      return {
+        stageMap: {
+          'feedback-creation': 'create',
+          'feedback-validation': 'validate',
+          'feedback-conflict-resolution': 'conflict'
+        }
+      }
+    },
+    computed: {
+      name () {
+        return this.$store.getters.resolveSubscriptionKeyToName(
+          {query_key: this.query_key, query_type: this.query_type})
+      }
+    },
+    methods: {
+      deactivate () {
+        this.$store.dispatch('deactivateSubscription', {pk: this.subscriptionPk}).then(() => {
+          if (this.$route.params.pk === this.subscriptionPk) {
+            this.$router.push('/')
+          }
+        }).catch(err => {
+          this.$notify({
+            title: `Unable to deactivate subscription ${this.name}`,
+            text: err.msg,
+            type: 'error'
+          })
+        })
+      }
+    }
+  }
+</script>
+
+<style scoped>
+</style>
diff --git a/frontend/src/components/subscriptions/SubscriptionList.vue b/frontend/src/components/subscriptions/SubscriptionList.vue
index f3e44747bd523c4b10d54a06cab3ed303b4b75f5..1c9864a8630481af3d835b29898a2d0f1dcee341 100644
--- a/frontend/src/components/subscriptions/SubscriptionList.vue
+++ b/frontend/src/components/subscriptions/SubscriptionList.vue
@@ -10,19 +10,23 @@
       <div v-for="item in subscriptionTypes" :key="item.type">
         <subscription-type
           v-bind="item"
-          :empty-subscription-type="subscriptions[item.type].length === 0"
+          :is-empty-subscription-type="subscriptions[item.type].length === 0"
           :possible-subscription-keys="possibleKeys[item.type]"
           @toggleExpand="item.expanded = !item.expanded"
         >
-          <subscription
-            v-for="subscription in subscriptions[item.type]"
-            :key="subscription.pk"
-            :subscription-pk="subscription.pk"
-            :feedback_stage="subscription.feedback_stage"
-            :query_key="subscription.query_key"
-          >
+          <div v-if="examTypesLoaded && submissionTypesLoaded">
+            <subscription-for-list
+              v-for="subscription in subscriptions[item.type]"
+              v-if="subscription.available > 0 && !subscription.deactivated"
+              :key="subscription.pk"
+              :subscription-pk="subscription.pk"
+              :feedback_stage="subscription.feedback_stage"
+              :query_key="subscription.query_key"
+              :query_type="subscription.query_type"
+            >
 
-          </subscription>
+            </subscription-for-list>
+          </div>
         </subscription-type>
       </div>
     </v-list>
@@ -33,11 +37,11 @@
   import {mapGetters, mapActions, mapState} from 'vuex'
   import SubscriptionCreation from '@/components/subscriptions/SubscriptionCreation'
   import SubscriptionType from '@/components/subscriptions/SubscriptionType'
-  import Subscription from '@/components/subscriptions/Subscription'
+  import SubscriptionForList from '@/components/subscriptions/SubscriptionForList'
 
   export default {
     components: {
-      Subscription,
+      SubscriptionForList,
       SubscriptionType,
       SubscriptionCreation},
     name: 'subscription-list',
@@ -50,6 +54,8 @@
     data () {
       return {
         subscriptionCreateMenu: {},
+        submissionTypesLoaded: false,
+        examTypesLoaded: false,
         subscriptionTypes: [
           {
             name: 'Random',
@@ -74,18 +80,6 @@
             type: 'submission_type',
             description: 'Just submissions for the specified type.',
             expanded: true
-          },
-          {
-            name: 'Student',
-            type: 'student',
-            description: 'The submissions of a student.',
-            expanded: true,
-            createPermission: () => {
-              return this.$store.getters.isReviewer
-            },
-            viewPermission: () => {
-              return this.$store.getters.isReviewer
-            }
           }
         ]
       }
@@ -99,10 +93,10 @@
       }),
       possibleKeys () {
         const submissionTypes = Object.entries(this.$store.state.submissionTypes).map(([id, type]) => {
-          return {text: type.name}
+          return {text: type.name, key: type.pk}
         })
         const examTypes = Object.entries(this.$store.state.examTypes).map(([id, type]) => {
-          return {text: type['module_reference']}
+          return {text: type['module_reference'], key: type.pk}
         })
         return {
           submission_type: submissionTypes,
@@ -123,11 +117,15 @@
         this.getSubscriptions()
       }
       if (Object.keys(this.$store.state.submissionTypes).length === 0) {
-        this.updateSubmissionTypes(['name'])
+        this.updateSubmissionTypes().then(() => { this.submissionTypesLoaded = true })
+      } else {
+        this.submissionTypesLoaded = true
       }
       if (Object.keys(this.$store.state.examTypes).length === 0 &&
         this.$store.getters.isReviewer) {
-        this.getExamTypes()
+        this.getExamTypes().then(() => { this.examTypesLoaded = true })
+      } else {
+        this.examTypesLoaded = true
       }
     }
   }
diff --git a/frontend/src/components/subscriptions/SubscriptionType.vue b/frontend/src/components/subscriptions/SubscriptionType.vue
index 0b59b87bdbb40294b36fc4c93f05c09d324efbec..cb735799047521f2a59b9f43fbe97118a029fad8 100644
--- a/frontend/src/components/subscriptions/SubscriptionType.vue
+++ b/frontend/src/components/subscriptions/SubscriptionType.vue
@@ -9,7 +9,7 @@
           {{ description }}
         </v-list-tile-sub-title>
       </v-list-tile-content>
-      <v-list-tile-action v-if="!emptySubscriptionType">
+      <v-list-tile-action v-if="!isEmptySubscriptionType">
         <v-btn icon @click="$emit('toggleExpand')">
           <v-icon v-if="expanded">keyboard_arrow_up</v-icon>
           <v-icon v-else>keyboard_arrow_down</v-icon>
@@ -62,7 +62,7 @@
         type: Boolean,
         default: true
       },
-      emptySubscriptionType: {
+      isEmptySubscriptionType: {
         type: Boolean,
         required: true
       },
diff --git a/frontend/src/components/tutor_list/TutorList.vue b/frontend/src/components/tutor_list/TutorList.vue
new file mode 100644
index 0000000000000000000000000000000000000000..bbc69b7f8854bef01cbd1c1546ecaafdf6e39606
--- /dev/null
+++ b/frontend/src/components/tutor_list/TutorList.vue
@@ -0,0 +1,54 @@
+<template>
+  <v-flex md4>
+    <v-data-table
+      :headers="headers"
+      :items="tutors"
+      :search="search"
+      item-key="name"
+      hide-actions
+    >
+      <template slot="items" slot-scope="props">
+        <td>{{props.item.username}}</td>
+        <td class="text-xs-right">{{props.item.feedback_created}}</td>
+        <td class="text-xs-right">{{props.item.feedback_validated}}</td>
+      </template>
+    </v-data-table>
+  </v-flex>
+</template>
+
+<script>
+  import {mapState} from 'vuex'
+
+  export default {
+    name: 'tutor-list',
+    data () {
+      return {
+        search: '',
+        headers: [
+          {
+            text: 'Name',
+            align: 'left',
+            value: 'username'
+          },
+          {
+            text: '# created',
+            value: 'feedback_created'
+          },
+          {
+            text: '# validated',
+            value: 'feedback_validated'
+          }
+        ]
+      }
+    },
+    computed: {
+      ...mapState([
+        'tutors'
+      ])
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>
diff --git a/frontend/src/pages/LayoutSelector.vue b/frontend/src/pages/LayoutSelector.vue
index 231a7d2974bc7d073af723b84697e049acec5a1b..df530c96e2fe025bff44ab28c6a2186dc67fa22c 100644
--- a/frontend/src/pages/LayoutSelector.vue
+++ b/frontend/src/pages/LayoutSelector.vue
@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div style="height: 100%;">
     <component :is="layout"></component>
     <v-content>
       <router-view></router-view>
@@ -11,9 +11,11 @@
   import {mapGetters} from 'vuex'
   import TutorLayout from '@/pages/tutor/TutorLayout'
   import StudentLayout from '@/pages/student/StudentLayout'
+  import ReviewerLayout from '@/pages/reviewer/ReviewerLayout'
 
   export default {
     components: {
+      ReviewerLayout,
       StudentLayout,
       TutorLayout},
     name: 'layout-selector',
@@ -28,6 +30,8 @@
           return 'student-layout'
         } else if (this.isTutor) {
           return 'tutor-layout'
+        } else if (this.isReviewer) {
+          return 'reviewer-layout'
         }
       }
     }
diff --git a/frontend/src/pages/Login.vue b/frontend/src/pages/Login.vue
index 1e6bdd9dfb452a2c561891bb741df9a321a843c3..0035b112db987b31df6cab7e66f6c2136f7a831e 100644
--- a/frontend/src/pages/Login.vue
+++ b/frontend/src/pages/Login.vue
@@ -1,36 +1,36 @@
 <template>
-      <v-container fill-height>
-        <v-layout align-center justify-center>
-          <v-flex text-xs-center xs8 sm6 md4 lg2>
-            <img src="../assets/brand.png"/>
-            <h3 class="pt-3">Log in</h3>
-            <v-alert
-              outline
-              v-if="msg"
-              color="error"
-              :value="true"
-              transition="fade-transition"
-            >{{ msg }}</v-alert>
-            <p v-else>But I corrected them, sir.</p>
-            <v-form
-              @submit="submit">
-              <v-text-field
-                label="Username"
-                v-model="credentials.username"
-                required
-                autofocus
-              />
-              <v-text-field
-                label="Password"
-                v-model="credentials.password"
-                type="password"
-                required
-                />
-              <v-btn :loading="loading" type="submit" color="primary">Access</v-btn>
-            </v-form>
-          </v-flex>
-        </v-layout>
-      </v-container>
+  <v-container fill-height>
+    <v-layout align-center justify-center>
+      <v-flex text-xs-center xs8 sm6 md4 lg2>
+        <img src="../assets/brand.png"/>
+        <h3 class="pt-3">Log in</h3>
+        <v-alert
+          outline
+          v-if="msg"
+          color="error"
+          :value="true"
+          transition="fade-transition"
+        >{{ msg }}</v-alert>
+        <p v-else>But I corrected them, sir.</p>
+        <v-form
+          @submit.prevent="submit">
+          <v-text-field
+            label="Username"
+            v-model="credentials.username"
+            required
+            autofocus
+          />
+          <v-text-field
+            label="Password"
+            v-model="credentials.password"
+            type="password"
+            required
+          />
+          <v-btn :loading="loading" type="submit" color="primary">Access</v-btn>
+        </v-form>
+      </v-flex>
+    </v-layout>
+  </v-container>
 </template>
 
 
diff --git a/frontend/src/pages/StartPageSelector.vue b/frontend/src/pages/StartPageSelector.vue
index bfcb052b630e1c896d1ba3f12b2369a388f3974a..b41f34ae82eefceb148b82263e514a7e4c5a78af 100644
--- a/frontend/src/pages/StartPageSelector.vue
+++ b/frontend/src/pages/StartPageSelector.vue
@@ -7,9 +7,11 @@
   import {mapGetters} from 'vuex'
   import TutorStartPage from '@/pages/tutor/TutorStartPage'
   import StudentPage from '@/pages/student/StudentPage'
+  import ReviewerStartPage from '@/pages/reviewer/ReviewerStartPage'
   export default {
     name: 'start-page-selector',
     components: {
+      ReviewerStartPage,
       StudentPage,
       TutorStartPage
     },
@@ -24,6 +26,8 @@
           return 'student-page'
         } else if (this.isTutor) {
           return 'tutor-start-page'
+        } else if (this.isReviewer) {
+          return 'reviewer-start-page'
         }
       }
     }
diff --git a/frontend/src/pages/SubscriptionWorkPage.vue b/frontend/src/pages/SubscriptionWorkPage.vue
index 6f1094d0b879b114096b424ab362df2013116ed1..62b0f4d332370ebd1f0fc696c9666198d4169c9d 100644
--- a/frontend/src/pages/SubscriptionWorkPage.vue
+++ b/frontend/src/pages/SubscriptionWorkPage.vue
@@ -7,6 +7,7 @@
         :assignment="currentAssignment"
         :key="subscription.pk"
         @feedbackCreated="startWorkOnNextAssignment"
+        @skip="skipAssignment"
         class="ma-4 autofocus"
       />
     </v-flex>
@@ -31,11 +32,11 @@
   function onRouteEnterOrUpdate (to, from, next) {
     if (to.name === 'subscription') {
       let subscription = store.state.subscriptions[to.params['pk']]
-      if (!subscription.currentAssignment) {
-        store.dispatch('getCurrentAssignment', subscription['pk']).then(() => {
+      if (subscription['assignments'].length === 0) {
+        store.dispatch('getAssignmentForSubscription', {subscription}).then(() => {
           next()
+          store.dispatch('getAssignmentForSubscription', {subscription})
         })
-        store.dispatch('getNextAssignment', subscription['pk'])
       } else {
         next()
       }
@@ -53,7 +54,7 @@
         return this.$store.state.subscriptions[this.$route.params['pk']]
       },
       currentAssignment () {
-        return this.subscription.currentAssignment
+        return this.subscription['assignments'][0]
       },
       submission () {
         return this.currentAssignment.submission
@@ -70,31 +71,30 @@
     },
     methods: {
       prefetchAssignment () {
-        this.$store.dispatch('getNextAssignment', this.subscription['pk']).catch(err => {
-          if (err.statusCode === 410) {
-            this.$notify({
-              title: 'Last submission here!',
-              text: 'This will be your last submission to correct for this subscription.',
-              type: 'warning'
-            })
-          }
+        this.$store.dispatch('getAssignmentForSubscription', {subscription: this.subscription}).catch(() => {
+          this.$notify({
+            title: 'Last submission here!',
+            text: 'This will be your last submission to correct for this subscription.',
+            type: 'warning'
+          })
         })
       },
       startWorkOnNextAssignment () {
-        if (this.subscription.nextAssignment) {
-          this.$store.commit(mut.UPDATE_ASSIGNMENT, {
-            key: 'currentAssignment',
-            assignment: this.subscription.nextAssignment,
-            subscriptionPk: this.subscription['pk']
-          })
-          this.prefetchAssignment()
-        } else {
+        this.$store.commit(mut.DELETE_ASSIGNMENT_FROM_SUBSCRIPTION_QUEUE, {
+          subscription: this.subscription
+        })
+        this.prefetchAssignment()
+      },
+      skipAssignment () {
+        this.$store.dispatch('deleteAssignment', {assignment: this.currentAssignment}).then(() => {
+          this.startWorkOnNextAssignment()
+        }).catch(err => {
           this.$notify({
-            title: 'This subscription has ended',
-            text: 'This subscription has ended',
-            type: 'info'
+            title: "Couldn't skip this submission",
+            text: err.msg,
+            type: 'error'
           })
-        }
+        })
       }
     }
   }
diff --git a/frontend/src/pages/base/TutorReviewerBaseLayout.vue b/frontend/src/pages/base/TutorReviewerBaseLayout.vue
new file mode 100644
index 0000000000000000000000000000000000000000..ca9866ce4a9659e01ff4acf721f9259f443cc012
--- /dev/null
+++ b/frontend/src/pages/base/TutorReviewerBaseLayout.vue
@@ -0,0 +1,57 @@
+<template>
+  <base-layout>
+
+    <template slot="header">
+      Grady
+    </template>
+
+    <template slot="sidebar-content">
+      <v-list dense>
+        <v-list-tile exact v-for="(item, i) in generalNavItems" :key="i" :to="item.route">
+          <v-list-tile-action>
+            <v-icon>{{ item.icon }}</v-icon>
+          </v-list-tile-action>
+          <v-list-tile-content>
+            <v-list-tile-title>
+              {{ item.name }}
+            </v-list-tile-title>
+          </v-list-tile-content>
+        </v-list-tile>
+      </v-list>
+      <v-divider></v-divider>
+      <slot name="above-subscriptions"></slot>
+      <subscription-list :sidebar="true"/>
+      <slot name="below-subscriptions"></slot>
+    </template>
+  </base-layout>
+</template>
+
+
+<script>
+  import BaseLayout from '@/components/BaseLayout'
+  import SubscriptionList from '@/components/subscriptions/SubscriptionList'
+
+  export default {
+    components: {
+      SubscriptionList,
+      BaseLayout},
+    name: 'tutor-reviewer-base-layout',
+    data () {
+      return {
+        generalNavItems: [
+          {
+            name: 'Overview',
+            icon: 'home',
+            route: '/home'
+          },
+          {
+            name: 'Progress',
+            icon: 'trending_up',
+            route: '/home'
+          }
+        ]
+      }
+    }
+  }
+</script>
+
diff --git a/frontend/src/pages/reviewer/ReviewerLayout.vue b/frontend/src/pages/reviewer/ReviewerLayout.vue
new file mode 100644
index 0000000000000000000000000000000000000000..1a56fb70b29f0fc114983dbac99cec8bc3783305
--- /dev/null
+++ b/frontend/src/pages/reviewer/ReviewerLayout.vue
@@ -0,0 +1,45 @@
+<template>
+    <tutor-reviewer-base-layout>
+      <v-list dense slot="above-subscriptions">
+        <v-list-tile v-for="(item, i) in subGeneralNavItems" :key="i" :to="item.route">
+          <v-list-tile-action>
+            <v-icon>{{ item.icon }}</v-icon>
+          </v-list-tile-action>
+          <v-list-tile-content>
+            <v-list-tile-title>
+              {{ item.name }}
+            </v-list-tile-title>
+          </v-list-tile-content>
+        </v-list-tile>
+      </v-list>
+    </tutor-reviewer-base-layout>
+</template>
+
+<script>
+  import TutorReviewerBaseLayout from '@/pages/base/TutorReviewerBaseLayout'
+
+  export default {
+    components: {TutorReviewerBaseLayout},
+    name: 'reviewer-layout',
+    data () {
+      return {
+        subGeneralNavItems: [
+          {
+            name: 'Students',
+            route: '/student-overview',
+            icon: 'people'
+          },
+          {
+            name: 'Tutors',
+            route: {name: 'tutor-overview'},
+            icon: 'people'
+          }
+        ]
+      }
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>
diff --git a/frontend/src/pages/reviewer/ReviewerPage.vue b/frontend/src/pages/reviewer/ReviewerPage.vue
deleted file mode 100644
index 1bcc4a334896da6ba29875885c1b38210c6c898b..0000000000000000000000000000000000000000
--- a/frontend/src/pages/reviewer/ReviewerPage.vue
+++ /dev/null
@@ -1,57 +0,0 @@
-<template>
-  <div>
-    <v-navigation-drawer persistent stateless value="true">
-    <v-toolbar flat>
-      <v-list class="pa-1">
-        <v-list-tile avatar>
-          <v-list-tile-avatar>
-            <img src="../../assets/brand.png" />
-          </v-list-tile-avatar>
-          <v-list-tile-content>
-            <v-list-tile-title class="title" >Grady Menu</v-list-tile-title>
-          </v-list-tile-content>
-        </v-list-tile>
-      </v-list>
-    </v-toolbar>
-    <v-divider></v-divider>
-    <v-list>
-      <v-list-tile v-for="item in items" :key="item.title" :to="item.to" @click="">
-        <v-list-tile-action>
-          <v-icon>{{ item.icon }}</v-icon>
-        </v-list-tile-action>
-        <v-list-tile-content>
-          <v-list-tile-title>{{ item.title }}</v-list-tile-title>
-        </v-list-tile-content>
-      </v-list-tile>
-    </v-list>
-  </v-navigation-drawer>
-
-    <p>
-      Was Geht ab?
-    </p>
-  </div>
-</template>
-
-<script>
-import ReviewerToolbar from './ReviewerToolbar.vue'
-
-export default {
-  components: {
-    ReviewerToolbar
-  },
-  name: 'reviewer-page',
-  data () {
-    return {
-      drawer: true,
-      items: [
-        {title: 'Student List', to: '/reviewer/student-overview'},
-        {title: 'Submission List', to: '/'}
-      ],
-      right: null
-    }
-  }
-}
-</script>
-
-<style lang="css" scoped>
-</style>
diff --git a/frontend/src/pages/reviewer/ReviewerStartPage.vue b/frontend/src/pages/reviewer/ReviewerStartPage.vue
new file mode 100644
index 0000000000000000000000000000000000000000..71f055328bf1e32b349119f6c56fc594251070d0
--- /dev/null
+++ b/frontend/src/pages/reviewer/ReviewerStartPage.vue
@@ -0,0 +1,17 @@
+<template>
+    <v-container fill-height>
+      <v-layout align-center justify-center>
+        <h1>You are reviewer!</h1>
+      </v-layout>
+    </v-container>
+</template>
+
+<script>
+  export default {
+    name: 'reviewer-start-page'
+  }
+</script>
+
+<style scoped>
+
+</style>
diff --git a/frontend/src/pages/reviewer/ReviewerStudentSubmissionPage.vue b/frontend/src/pages/reviewer/ReviewerStudentSubmissionPage.vue
new file mode 100644
index 0000000000000000000000000000000000000000..822be371e692e15a76c59f9f9062d9b05a535149
--- /dev/null
+++ b/frontend/src/pages/reviewer/ReviewerStudentSubmissionPage.vue
@@ -0,0 +1,59 @@
+<template>
+  <submission-correction
+    :submission-without-assignment="submission"
+    :feedback="submission.feedback"
+  ></submission-correction>
+</template>
+
+<script>
+  import store from '@/store/store'
+  import VueInstance from '@/main'
+  import SubmissionCorrection from '@/components/submission_notes/SubmissionCorrection'
+
+  function onRouteEnterOrUpdate (to, from, next) {
+    if (to.name === 'submission-side-view') {
+      let submission = store.state.submissions[to.params.pk]
+      if (!submission) {
+        store.dispatch('getSubmissionFeedbackTest', {pk: to.params.pk}).then(() => {
+          VueInstance.$nextTick(() => {
+            next()
+          })
+        }).catch(() => {
+          VueInstance.$notify({
+            title: 'Error',
+            text: 'Unable to fetch student data',
+            type: 'error'
+          })
+          next(false)
+        })
+      } else {
+        next()
+      }
+    } else {
+      next()
+    }
+  }
+
+  export default {
+    components: {SubmissionCorrection},
+    name: 'reviewer-student-submission-page',
+    computed: {
+      submissionPk () {
+        return this.$route.params['pk']
+      },
+      submission () {
+        return this.$store.state.submissions[this.submissionPk]
+      }
+    },
+    beforeRouteEnter (to, from, next) {
+      onRouteEnterOrUpdate(to, from, next)
+    },
+    beforeRouteUpdate (to, from, next) {
+      onRouteEnterOrUpdate(to, from, next)
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>
diff --git a/frontend/src/pages/reviewer/ReviewerToolbar.vue b/frontend/src/pages/reviewer/ReviewerToolbar.vue
deleted file mode 100644
index 8c547490d824b9aa68d6c209f221d3a2ed543ac3..0000000000000000000000000000000000000000
--- a/frontend/src/pages/reviewer/ReviewerToolbar.vue
+++ /dev/null
@@ -1,20 +0,0 @@
-<template>
-  <v-toolbar>
-    <v-toolbar-items>
-      <v-list-tile-avatar>
-        <img src="../../assets/brand.png">
-      </v-list-tile-avatar>
-    </v-toolbar-items>
-    <v-toolbar-title>Grady</v-toolbar-title>
-    <v-spacer></v-spacer>
-  </v-toolbar>
-</template>
-
-<script>
-export default {
-  name: 'reviewer-toolbar'
-}
-</script>
-
-<style scoped>
-</style>
diff --git a/frontend/src/pages/reviewer/StudentListOverview.vue b/frontend/src/pages/reviewer/StudentListOverview.vue
deleted file mode 100644
index 08ae4caff8a21e2869f8c85cedc679a99ee451d4..0000000000000000000000000000000000000000
--- a/frontend/src/pages/reviewer/StudentListOverview.vue
+++ /dev/null
@@ -1,21 +0,0 @@
-<template>
-  <p>
-    Whack o !
-  </p>
-</template>
-
-<script>
-export default {
-
-  name: 'StudentListOverview',
-
-  data () {
-    return {
-
-    }
-  }
-}
-</script>
-
-<style lang="css" scoped>
-</style>
diff --git a/frontend/src/pages/reviewer/StudentOverviewPage.vue b/frontend/src/pages/reviewer/StudentOverviewPage.vue
new file mode 100644
index 0000000000000000000000000000000000000000..8b0936fbe696d53de6966347775cdd8ff38c4bb1
--- /dev/null
+++ b/frontend/src/pages/reviewer/StudentOverviewPage.vue
@@ -0,0 +1,23 @@
+<template>
+    <v-layout>
+      <v-flex xs6>
+        <student-list class="ma-1"></student-list>
+      </v-flex>
+      <v-flex xs6 style="height: 100%;">
+        <router-view></router-view>
+      </v-flex>
+    </v-layout>
+</template>
+
+<script>
+  import StudentList from '@/components/student_list/StudentList'
+
+  export default {
+    components: {StudentList},
+    name: 'student-overview-page'
+  }
+</script>
+
+<style scoped>
+
+</style>
diff --git a/frontend/src/pages/reviewer/TutorOverviewPage.vue b/frontend/src/pages/reviewer/TutorOverviewPage.vue
new file mode 100644
index 0000000000000000000000000000000000000000..88109335ca30197f5c64959f2eea75636c1324d9
--- /dev/null
+++ b/frontend/src/pages/reviewer/TutorOverviewPage.vue
@@ -0,0 +1,21 @@
+<template>
+  <tutor-list></tutor-list>
+</template>
+
+<script>
+  import store from '@/store/store'
+  import TutorList from '@/components/tutor_list/TutorList'
+
+  export default {
+    components: {TutorList},
+    name: 'tutor-overview-page',
+    beforeRouteEnter (to, from, next) {
+      store.dispatch('getTutors')
+      next()
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>
diff --git a/frontend/src/pages/student/StudentSubmissionPage.vue b/frontend/src/pages/student/StudentSubmissionPage.vue
index e7946ff806e9e72e7977c321703e00e3ff385e9f..b751e545159d569d1159d8fc583864b5c7e5adbf 100644
--- a/frontend/src/pages/student/StudentSubmissionPage.vue
+++ b/frontend/src/pages/student/StudentSubmissionPage.vue
@@ -81,8 +81,8 @@
     methods: {
       onRouteMountOrUpdate (routeId) {
         this.$store.commit(studentPageMut.SET_VISITED, { index: routeId, visited: true })
-        this.$store.commit('submissionNotes/' + subNotesMut.SET_RAW_SUBMISSION,
-          this.$store.state.studentPage.submissionData[this.id].text)
+        this.$store.commit('submissionNotes/' + subNotesMut.SET_SUBMISSION,
+          this.$store.state.studentPage.submissionData[this.id])
       }
     },
     mounted () {
diff --git a/frontend/src/pages/tutor/TutorLayout.vue b/frontend/src/pages/tutor/TutorLayout.vue
index df21a894386de685714d3b940b91415b98ac3ee9..97886c9dc807868cecc8f1cc03b3f66ad565d391 100644
--- a/frontend/src/pages/tutor/TutorLayout.vue
+++ b/frontend/src/pages/tutor/TutorLayout.vue
@@ -1,55 +1,16 @@
 <template>
-  <base-layout @sidebarMini="mini = $event">
-
-    <template slot="header">
-      Grady
-    </template>
-
-    <v-list dense slot="sidebar-content">
-      <v-list-tile exact v-for="(item, i) in generalNavItems" :key="i" :to="item.route">
-        <v-list-tile-action>
-          <v-icon>{{ item.icon }}</v-icon>
-        </v-list-tile-action>
-        <v-list-tile-content>
-          <v-list-tile-title>
-            {{ item.name }}
-          </v-list-tile-title>
-        </v-list-tile-content>
-      </v-list-tile>
-
-      <v-divider></v-divider>
-
-      <subscription-list :sidebar="true"/>
-    </v-list>
-  </base-layout>
+  <tutor-reviewer-base-layout></tutor-reviewer-base-layout>
 </template>
 
 
 <script>
-  import BaseLayout from '@/components/BaseLayout'
-  import SubscriptionList from '@/components/subscriptions/SubscriptionList'
+  import TutorReviewerBaseLayout from '@/pages/base/TutorReviewerBaseLayout'
 
   export default {
     components: {
-      SubscriptionList,
-      BaseLayout},
-    name: 'tutor-layout',
-    data () {
-      return {
-        generalNavItems: [
-          {
-            name: 'Overview',
-            icon: 'home',
-            route: '/home'
-          },
-          {
-            name: 'Progress',
-            icon: 'trending_up',
-            route: '/home'
-          }
-        ]
-      }
-    }
+      TutorReviewerBaseLayout
+    },
+    name: 'tutor-layout'
   }
 </script>
 
diff --git a/frontend/src/pages/tutor/TutorStartPage.vue b/frontend/src/pages/tutor/TutorStartPage.vue
index 1a98f924261130743f62ecc22cf241c28f96e462..1971e2679b7456779196ffed93b99fa700540905 100644
--- a/frontend/src/pages/tutor/TutorStartPage.vue
+++ b/frontend/src/pages/tutor/TutorStartPage.vue
@@ -1,6 +1,5 @@
 <template>
     <v-flex lg3>
-      <subscription-list/>
     </v-flex>
 </template>
 
@@ -9,10 +8,7 @@
 
   export default {
     components: {SubscriptionList},
-    name: 'tutor-start-page',
-    mounted () {
-      this.$store.dispatch('updateSubmissionTypes')
-    }
+    name: 'tutor-start-page'
   }
 </script>
 
diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js
index c49a411d62816e0e67ddb4da1926eaf2884b0e25..d2d4cb5144fcc8e3845cb1e8a5e78b287f70dd9b 100644
--- a/frontend/src/router/index.js
+++ b/frontend/src/router/index.js
@@ -2,27 +2,57 @@ import Vue from 'vue'
 import Router from 'vue-router'
 import Login from '@/pages/Login'
 import StudentSubmissionPage from '@/pages/student/StudentSubmissionPage'
+import StudentOverviewPage from '@/pages/reviewer/StudentOverviewPage'
+import TutorOverviewPage from '@/pages/reviewer/TutorOverviewPage'
 import SubscriptionWorkPage from '@/pages/SubscriptionWorkPage'
 import PageNotFound from '@/pages/PageNotFound'
 import StartPageSelector from '@/pages/StartPageSelector'
 import LayoutSelector from '@/pages/LayoutSelector'
+import ReviewerStudentSubmissionPage from '@/pages/reviewer/ReviewerStudentSubmissionPage'
+import StudentListHelpCard from '@/components/student_list/StudentListHelpCard'
 import VueInstance from '@/main'
-
 import store from '@/store/store'
 
 Vue.use(Router)
 
+function denyAccess (next, redirect) {
+  next(redirect.path)
+  VueInstance.$notify({
+    title: 'Access denied',
+    text: "You don't have permission to view this.",
+    type: 'error'
+  })
+}
+
 function tutorOrReviewerOnly (to, from, next) {
-  next()
   if (store.getters.isTutorOrReviewer) {
     next()
   } else {
-    next(from.path)
-    VueInstance.$notify({
-      title: 'Access denied',
-      text: "You don't have permission to view this.",
-      type: 'error'
-    })
+    denyAccess(next, from.path)
+  }
+}
+
+function reviewerOnly (to, from, next) {
+  if (store.getters.isReviewer) {
+    next()
+  } else {
+    denyAccess(next, from.path)
+  }
+}
+
+function studentOnly (to, from, next) {
+  if (store.getters.isStudent) {
+    next()
+  } else {
+    next(false)
+  }
+}
+
+function checkLoggedIn (to, from, next) {
+  if (store.getters.isLoggedIn) {
+    next()
+  } else {
+    next('/login/')
   }
 }
 
@@ -36,6 +66,7 @@ const router = new Router({
     {
       path: '',
       redirect: 'home',
+      beforeEnter: checkLoggedIn,
       component: LayoutSelector,
       children: [
         {
@@ -49,8 +80,32 @@ const router = new Router({
           beforeEnter: tutorOrReviewerOnly,
           component: SubscriptionWorkPage
         },
+        {
+          path: 'student-overview',
+          beforeEnter: reviewerOnly,
+          component: StudentOverviewPage,
+          children: [
+            {
+              path: '',
+              name: 'student-overview',
+              component: StudentListHelpCard
+            },
+            {
+              path: 'submission/:pk',
+              name: 'submission-side-view',
+              component: ReviewerStudentSubmissionPage
+            }
+          ]
+        },
+        {
+          path: 'tutor-overview',
+          name: 'tutor-overview',
+          beforeEnter: reviewerOnly,
+          component: TutorOverviewPage
+        },
         {
           path: 'submission/:id',
+          beforeEnter: studentOnly,
           component: StudentSubmissionPage
         }
       ]
diff --git a/frontend/src/store/actions.js b/frontend/src/store/actions.js
index 52ac528f9618229e5f9ab2e72be5ed6e2fa5b289..5cf10e161ba9017478768f7b76f7e3dec29b01f7 100644
--- a/frontend/src/store/actions.js
+++ b/frontend/src/store/actions.js
@@ -4,9 +4,9 @@ import {subNotesMut} from '@/store/modules/submission-notes'
 import * as api from '@/api'
 import router from '@/router/index'
 
-function passErrorIfResponsePresent (err, fallbackMsg) {
+function passErrorIfNoResponse (err, fallbackMsg) {
   if (err.response) {
-    throw err
+    throw new Error(err.response.data)
   } else {
     if (fallbackMsg) {
       throw new Error(fallbackMsg)
@@ -20,7 +20,7 @@ const actions = {
       const subscriptions = await api.fetchSubscriptions()
       commit(mut.SET_SUBSCRIPTIONS, subscriptions)
     } catch (err) {
-      passErrorIfResponsePresent(err, 'Unable to fetch subscriptions')
+      passErrorIfNoResponse(err, 'Unable to fetch subscriptions')
     }
   },
   async getExamTypes ({commit}) {
@@ -28,7 +28,7 @@ const actions = {
       const examTypes = await api.fetchExamType({})
       commit(mut.SET_EXAM_TYPES, examTypes)
     } catch (err) {
-      passErrorIfResponsePresent(err, 'Unable to fetch exam mut')
+      passErrorIfNoResponse(err, 'Unable to fetch exam mut')
     }
   },
   async subscribeTo ({ commit }, {type, key, stage}) {
@@ -36,7 +36,16 @@ const actions = {
       const subscription = await api.subscribeTo(type, key, stage)
       commit(mut.SET_SUBSCRIPTION, subscription)
     } catch (err) {
-      passErrorIfResponsePresent(err, 'Subscribing unsuccessful')
+      passErrorIfNoResponse(err, 'Subscribing unsuccessful')
+    }
+  },
+  async deactivateSubscription ({commit}, {subscription, pk}) {
+    try {
+      const subscriptionPk = subscription ? subscription.pk : pk
+      await api.deactivateSubscription({pk: subscriptionPk})
+      commit(mut.SET_SUBSCRIPTION_DEACTIVATED, {pk: subscriptionPk})
+    } catch (err) {
+      passErrorIfNoResponse(err, 'Unable to deactivate subscription')
     }
   },
   async updateSubmissionTypes ({ commit }, fields) {
@@ -46,33 +55,52 @@ const actions = {
         commit(mut.UPDATE_SUBMISSION_TYPE, type)
       })
     } catch (err) {
-      passErrorIfResponsePresent(err)
+      passErrorIfNoResponse(err)
     }
   },
-  async getCurrentAssignment ({ commit }, subscriptionPk) {
+  async getAssignmentForSubscription ({commit, state}, {subscription}) {
+    if (subscription.assignments.length < 2) {
+      try {
+        const assignment = await api.createAssignment({subscription})
+        commit(mut.ADD_ASSIGNMENT_TO_SUBSCRIPTION_QUEUE, {assignment})
+        return assignment
+      } catch (err) {
+        passErrorIfNoResponse(err, "Couldn't fetch assignment")
+      }
+    } else {
+      return subscription.assignments[0]
+    }
+  },
+  async deleteAssignment ({commit, state}, {assignment}) {
     try {
-      const assignment = await api.fetchCurrentAssignment(subscriptionPk)
-      commit(mut.UPDATE_ASSIGNMENT, {
-        assignment,
-        subscriptionPk,
-        key: 'currentAssignment'
-      })
-      return assignment
+      return await api.deleteAssignment({assignment})
     } catch (err) {
-      passErrorIfResponsePresent(err, "Couldn't fetch assignment")
+      passErrorIfNoResponse(err, 'Unable to delete assignment')
     }
   },
-  async getNextAssignment ({ commit }, subscriptionPk) {
+  async getStudents ({commit}) {
     try {
-      const assignment = await api.fetchNextAssignment(subscriptionPk)
-      commit(mut.UPDATE_ASSIGNMENT, {
-        assignment,
-        subscriptionPk,
-        key: 'nextAssignment'
-      })
-      return assignment
+      const students = await api.fetchAllStudents()
+      commit(mut.SET_STUDENTS, students)
+      return students
+    } catch (err) {
+      passErrorIfNoResponse(err, 'Unable to fetch student data')
+    }
+  },
+  async getTutors ({commit}) {
+    try {
+      const tutors = await api.fetchAllTutors()
+      commit(mut.SET_TUTORS, tutors)
+    } catch (err) {
+      passErrorIfNoResponse(err, 'Unable to fetch tutor data.')
+    }
+  },
+  async getSubmissionFeedbackTest ({commit}, {pk}) {
+    try {
+      const submission = await api.fetchSubmissionFeedbackTests({pk})
+      commit(mut.SET_SUBMISSION, submission)
     } catch (err) {
-      passErrorIfResponsePresent(err, "Couldn't fetch assignment")
+      passErrorIfNoResponse(err, 'Unable to fetch submission')
     }
   },
   logout ({ commit }, message = '') {
diff --git a/frontend/src/store/getters.js b/frontend/src/store/getters.js
index 2f6c3e32e941c1732b25460f6a87177791740f1b..b5dfb59231748b191912c9cb2ad21038f325413e 100644
--- a/frontend/src/store/getters.js
+++ b/frontend/src/store/getters.js
@@ -1,5 +1,5 @@
 const getters = {
-  getSubscriptionsGroupedByType (state) {
+  getSubscriptionsGroupedByType (state, getters) {
     let subscriptions = {
       'random': [],
       'student': [],
@@ -12,12 +12,12 @@ const getters = {
     // sort the resulting arrays in subscriptions lexicographically by their query_keys
     Object.entries(subscriptions).forEach(([id, arr]) => {
       if (arr.length > 1 && arr[0].hasOwnProperty('query_key')) {
-        arr.sort((a, b) => {
-          const aLower = a['query_key'].toLowerCase()
-          const bLower = b['query_key'].toLowerCase()
-          if (aLower < bLower) {
+        arr.sort((subA, subB) => {
+          const subALower = getters.resolveSubscriptionKeyToName(subA).toLowerCase()
+          const subBLower = getters.resolveSubscriptionKeyToName(subB).toLowerCase()
+          if (subALower < subBLower) {
             return -1
-          } else if (aLower > bLower) {
+          } else if (subALower > subBLower) {
             return 1
           } else {
             return 0
@@ -27,6 +27,16 @@ const getters = {
     })
     return subscriptions
   },
+
+  resolveSubscriptionKeyToName: state => subscription => {
+    if (subscription.query_type === 'random') {
+      return 'Active'
+    } else if (subscription.query_type === 'exam') {
+      return state.examTypes[subscription.query_key].module_reference
+    } else if (subscription.query_type === 'submission_type') {
+      return state.submissionTypes[subscription.query_key].name
+    }
+  },
   getSubmission: state => pk => {
     return state.submissions[pk]
   },
diff --git a/frontend/src/store/modules/authentication.js b/frontend/src/store/modules/authentication.js
index a4972a487a2d1cf7a31ae9ab615331e6a5398d01..43b860652ad37db65db9397817ef50f96ad24ee6 100644
--- a/frontend/src/store/modules/authentication.js
+++ b/frontend/src/store/modules/authentication.js
@@ -4,7 +4,7 @@ import gradySays from '../grady_speak'
 function initialState () {
   return {
     token: sessionStorage.getItem('token'),
-    tokenCreationTime: 0,
+    lastTokenRefreshTry: 0,
     refreshingToken: false,
     username: '',
     jwtTimeDelta: 0,
@@ -19,6 +19,7 @@ export const authMut = Object.freeze({
   SET_JWT_TIME_DELTA: 'SET_JWT_TIME_DELTA',
   SET_USERNAME: 'SET_USERNAME',
   SET_USER_ROLE: 'SET_USER_ROLE',
+  SET_LAST_TOKEN_REFRESH_TRY: 'SET_LAST_TOKEN_REFRESH_TRY',
   RESET_STATE: 'RESET_STATE',
   SET_REFRESHING_TOKEN: 'SET_REFRESHING_TOKEN'
 })
@@ -50,7 +51,6 @@ const authentication = {
     [authMut.SET_JWT_TOKEN]: function (state, token) {
       sessionStorage.setItem('token', token)
       state.token = token
-      state.tokenCreationTime = Date.now()
     },
     [authMut.SET_JWT_TIME_DELTA]: function (state, timeDelta) {
       state.jwtTimeDelta = timeDelta
@@ -61,12 +61,15 @@ const authentication = {
     [authMut.SET_USER_ROLE]: function (state, userRole) {
       state.userRole = userRole
     },
+    [authMut.SET_REFRESHING_TOKEN]: function (state, refreshing) {
+      state.refreshingToken = refreshing
+    },
+    [authMut.SET_LAST_TOKEN_REFRESH_TRY]: function (state) {
+      state.lastTokenRefreshTry = Date.now()
+    },
     [authMut.RESET_STATE]: function (state) {
       sessionStorage.setItem('token', '')
       Object.assign(state, initialState())
-    },
-    [authMut.SET_REFRESHING_TOKEN]: function (state, refreshing) {
-      state.refreshingToken = refreshing
     }
   },
   actions: {
@@ -95,6 +98,7 @@ const authentication = {
         commit(authMut.SET_JWT_TOKEN, token)
       } finally {
         commit(authMut.SET_REFRESHING_TOKEN, false)
+        commit(authMut.SET_LAST_TOKEN_REFRESH_TRY)
       }
     },
     async getUserRole ({commit}) {
diff --git a/frontend/src/store/modules/submission-notes.js b/frontend/src/store/modules/submission-notes.js
index a63fd12016fcd40fd3ea66a239cb776cedcdef2e..2bb4106491a834115fd227dfcdc2acb526ce6b11 100644
--- a/frontend/src/store/modules/submission-notes.js
+++ b/frontend/src/store/modules/submission-notes.js
@@ -5,7 +5,7 @@ import {nameSpacer} from '@/store/util/helpers'
 export const subNotesNamespace = nameSpacer('submissionNotes/')
 
 export const subNotesMut = Object.freeze({
-  SET_RAW_SUBMISSION: 'SET_RAW_SUBMISSION',
+  SET_SUBMISSION: 'SET_SUBMISSION',
   SET_ORIG_FEEDBACK: 'SET_ORIG_FEEDBACK',
   UPDATE_FEEDBACK_LINE: 'UPDATE_FEEDBACK_LINE',
   UPDATE_FEEDBACK_SCORE: 'UPDATE_FEEDBACK_SCORE',
@@ -17,18 +17,21 @@ export const subNotesMut = Object.freeze({
 function initialState () {
   return {
     assignment: '',
+    isFeedbackCreation: false,
+    submission: {
+      text: ''
+    },
     ui: {
       showEditorOnLine: {},
       selectedCommentOnLine: {}
     },
-    orig: {
-      rawSubmission: '',
+    origFeedback: {
       score: null,
-      feedbackLines: {}
+      feedback_lines: {}
     },
-    updated: {
+    updatedFeedback: {
       score: null,
-      feedbackLines: {}
+      feedback_lines: {}
     }
   }
 }
@@ -37,42 +40,43 @@ const submissionNotes = {
   namespaced: true,
   state: initialState(),
   getters: {
-    // reduce the string rawSubmission into an object where the keys are the
+    // reduce the string submission.text into an object where the keys are the
     // line indexes starting at one and the values the corresponding submission line
     // this makes iterating over the submission much more pleasant
     submission: state => {
-      return state.orig.rawSubmission.split('\n').reduce((acc, cur, index) => {
+      return state.submission.text.split('\n').reduce((acc, cur, index) => {
         acc[index + 1] = cur
         return acc
       }, {})
     },
     score: state => {
-      return state.updated.score !== null ? state.updated.score : state.orig.score
+      return state.updatedFeedback.score !== null ? state.updatedFeedback.score : state.origFeedback.score
     },
     openEditorOrWrittenFeedback: state => {
       const openEdit = Object.values(state.ui.showEditorOnLine).some(bool => bool)
-      const hasWrittenFeedback = Object.keys(state.updated.feedbackLines).length > 0
+      const hasWrittenFeedback = Object.keys(state.updatedFeedback.feedback_lines).length > 0
       return openEdit || hasWrittenFeedback
     }
   },
   mutations: {
-    [subNotesMut.SET_RAW_SUBMISSION]: function (state, submission) {
-      state.orig.rawSubmission = submission
+    [subNotesMut.SET_SUBMISSION]: function (state, submission) {
+      state.submission = submission
     },
     [subNotesMut.SET_ORIG_FEEDBACK]: function (state, feedback) {
       if (feedback) {
-        state.orig.feedbackLines = feedback['feedback_lines'] ? feedback['feedback_lines'] : {}
-        state.orig.score = feedback.score
+        state.origFeedback = feedback
+      } else {
+        state.isFeedbackCreation = true
       }
     },
     [subNotesMut.UPDATE_FEEDBACK_LINE]: function (state, feedback) {
-      Vue.set(state.updated.feedbackLines, feedback.lineNo, feedback.comment)
+      Vue.set(state.updatedFeedback.feedback_lines, feedback.lineNo, feedback.comment)
     },
     [subNotesMut.UPDATE_FEEDBACK_SCORE]: function (state, score) {
-      state.updated.score = score
+      state.updatedFeedback.score = score
     },
     [subNotesMut.DELETE_FEEDBACK_LINE]: function (state, lineNo) {
-      Vue.delete(state.updated.feedbackLines, lineNo)
+      Vue.delete(state.updatedFeedback.feedback_lines, lineNo)
     },
     [subNotesMut.TOGGLE_EDITOR_ON_LINE]: function (state, {lineNo, comment}) {
       Vue.set(state.ui.selectedCommentOnLine, lineNo, comment)
@@ -83,17 +87,25 @@ const submissionNotes = {
     }
   },
   actions: {
-    submitFeedback: async function ({state}, assignment) {
-      let feedback = {}
-      if (Object.keys(state.updated.feedbackLines).length > 0) {
-        feedback['feedback_lines'] = state.updated.feedbackLines
+    submitFeedback: async function ({state}, {isFinal = false}) {
+      let feedback = {
+        is_final: isFinal,
+        of_submission: state.submission.pk
+      }
+      if (Object.keys(state.updatedFeedback.feedback_lines).length > 0) {
+        feedback['feedback_lines'] = state.updatedFeedback.feedback_lines
       }
-      if (state.orig.score === null && state.updated.score === null) {
+      if (state.origFeedback.score === null && state.updatedFeedback.score === null) {
         throw new Error('You need to give a score.')
-      } else if (state.updated.score !== null) {
-        feedback['score'] = state.updated.score
+      } else if (state.updatedFeedback.score !== null) {
+        feedback['score'] = state.updatedFeedback.score
+      }
+      if (state.isFeedbackCreation) {
+        return api.submitFeedbackForAssignment({feedback})
+      } else {
+        feedback.pk = state.origFeedback.pk
+        return api.submitUpdatedFeedback({feedback})
       }
-      return api.submitFeedbackForAssignment(feedback, assignment['pk'])
     }
   }
 }
diff --git a/frontend/src/store/mutations.js b/frontend/src/store/mutations.js
index 00a02fcd782b03cf29f2db216498b2d96cf19ba9..48c583ce7b0b7412d1389d5ab5d5d9155bf91e0b 100644
--- a/frontend/src/store/mutations.js
+++ b/frontend/src/store/mutations.js
@@ -3,18 +3,25 @@ import Vue from 'vue'
 import {initialState} from '@/store/store'
 
 export const mut = Object.freeze({
+  ADD_ASSIGNMENT_TO_SUBSCRIPTION_QUEUE: 'ADD_ASSIGNMENT_TO_SUBSCRIPTION_QUEUE',
+  DELETE_ASSIGNMENT_FROM_SUBSCRIPTION_QUEUE: 'DELETE_ASSIGNMENT_FROM_SUBSCRIPTION_QUEUE',
   SET_ASSIGNMENT: 'SET_ASSIGNMENT',
   SET_SUBSCRIPTIONS: 'SET_SUBSCRIPTIONS',
   SET_SUBSCRIPTION: 'SET_SUBSCRIPTION',
-  UPDATE_SUBMISSION_TYPE: 'UPDATE_SUBMISSION_TYPE',
-  UPDATE_ASSIGNMENT: 'UPDATE_ASSIGNMENT',
-  RESET_STATE: 'RESET_STATE',
+  SET_SUBSCRIPTION_DEACTIVATED: 'SET_SUBSCRIPTION_DEACTIVATED',
   SET_LAST_INTERACTION: 'SET_LAST_INTERACTION',
   SET_EXAM_TYPES: 'SET_EXAM_TYPES',
-  SET_NOTIFY_MESSAGE: 'SET_NOTIFY_MESSAGE'
+  SET_STUDENTS: 'SET_STUDENTS',
+  SET_TUTORS: 'SET_TUTORS',
+  SET_SUBMISSION: 'SET_SUBMISSION',
+  UPDATE_SUBMISSION_TYPE: 'UPDATE_SUBMISSION_TYPE',
+  RESET_STATE: 'RESET_STATE'
 })
 
 const mutations = {
+  [mut.SET_EXAM_TYPES] (state, examTypes) {
+    state.examTypes = examTypes
+  },
   [mut.SET_ASSIGNMENT] (state, assignment) {
     Vue.set(state.assignments, assignment.pk, assignment)
   },
@@ -27,6 +34,18 @@ const mutations = {
   [mut.SET_SUBSCRIPTION] (state, subscription) {
     Vue.set(state.subscriptions, subscription.pk, subscription)
   },
+  [mut.SET_SUBSCRIPTION_DEACTIVATED] (state, {pk}) {
+    state.subscriptions[pk].deactivated = true
+  },
+  [mut.SET_STUDENTS] (state, students) {
+    state.students = students
+  },
+  [mut.SET_TUTORS] (state, tutors) {
+    state.tutors = tutors
+  },
+  [mut.SET_SUBMISSION] (state, submission) {
+    Vue.set(state.submissions, submission.pk, submission)
+  },
   [mut.UPDATE_SUBMISSION_TYPE] (state, submissionType) {
     const updatedSubmissionType = {
       ...state.submissionTypes[submissionType.pk],
@@ -34,28 +53,19 @@ const mutations = {
     }
     Vue.set(state.submissionTypes, submissionType.pk, updatedSubmissionType)
   },
-  [mut.UPDATE_ASSIGNMENT] (state, {key, assignment, subscriptionPk}) {
-    const submission = assignment.submission
-    const feedback = assignment.feedback
-    let updatedAssignment = {
-      ...state.assignments[assignment.pk],
-      ...assignment
-    }
-    if (feedback) {
-      Vue.set(state.feedback, feedback.pk, feedback)
-    }
-    Vue.set(state.assignments, assignment.pk, updatedAssignment)
-    Vue.set(state.submissions, submission.pk, submission)
-    Vue.set(state.subscriptions[subscriptionPk], key, updatedAssignment)
+  [mut.ADD_ASSIGNMENT_TO_SUBSCRIPTION_QUEUE] (state, {assignment}) {
+    let subscription = state.subscriptions[assignment.subscription]
+    subscription['assignments'].push(assignment)
   },
-  [mut.RESET_STATE] (state) {
-    Object.assign(state, initialState())
+  [mut.DELETE_ASSIGNMENT_FROM_SUBSCRIPTION_QUEUE] (state, {subscription}) {
+    subscription.assignments.shift()
   },
+
   [mut.SET_LAST_INTERACTION] (state) {
     state.lastAppInteraction = Date.now()
   },
-  [mut.SET_EXAM_TYPES] (state, examTypes) {
-    state.examTypes = examTypes
+  [mut.RESET_STATE] (state) {
+    Object.assign(state, initialState())
   }
 }
 
diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js
index 09af47903cae284c2322f2a09481df7cefe7f1cf..55416db15cf0392e5119fd5a8744533af7a52b5c 100644
--- a/frontend/src/store/store.js
+++ b/frontend/src/store/store.js
@@ -22,7 +22,9 @@ export function initialState () {
     submissions: {},
     feedback: {},
     subscriptions: {},
-    assignments: {}
+    assignments: {},
+    students: [],
+    tutors: []
   }
 }
 
@@ -44,8 +46,9 @@ const store = new Vuex.Store({
       // authentication.token is manually saved since using it with this plugin caused issues
       // when manually reloading the page
       paths: Object.keys(initialState()).concat(
-        ['ui', 'studentPage', 'submissionNotes', 'authentication.username', 'authentication.userRole',
-          'authentication.jwtTimeDelta'])
+        ['ui', 'studentPage', 'submissionNotes', 'authentication.username',
+          'authentication.userRole', 'authentication.jwtTimeDelta',
+          'authentication.tokenCreationTime'])
     }),
     lastInteraction],
   actions,
diff --git a/grady/urls.py b/grady/urls.py
index 6d84db555ba003d80d095563ac6ab0d4fbd97129..8e651c92f337a55689fc2af3e6a79a94df55251f 100644
--- a/grady/urls.py
+++ b/grady/urls.py
@@ -6,9 +6,10 @@ from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token
 urlpatterns = [
     path('admin/', admin.site.urls),
     path('api/', include('core.urls')),
+    path('api/get-token/', obtain_jwt_token),
+    path('api/refresh-token/', refresh_jwt_token),
     path('api-auth/', include('rest_framework.urls',
                               namespace='rest_framework')),
-    path('api-token-auth/', obtain_jwt_token),
-    path('api-token-refresh/', refresh_jwt_token),
-    path('', TemplateView.as_view(template_name='index.html')),
+    path('', TemplateView.as_view(template_name='index.html'))
+
 ]
diff --git a/requirements.txt b/requirements.txt
index 9881225c11c7103a2c39218cbeb3b085134e7520..9f22e122200338b0f281b62485df5bdaa91b3a90 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,3 +9,4 @@ gunicorn~=19.7.0
 psycopg2-binary~=2.7.4
 whitenoise~=3.3.1
 xlrd~=1.0.0
+tqdm~=4.19.5