From 9ce91de10176aa1f286a03f8f723a0ed2d35df9c Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Thu, 9 Aug 2018 11:26:28 +0200
Subject: [PATCH 1/9] Finnaly change max-line-length to 100!!!!

---
 setup.cfg | 7 +++++++
 1 file changed, 7 insertions(+)
 create mode 100644 setup.cfg

diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 00000000..68c89dfb
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,7 @@
+[flake8]
+
+max-line-length = 100
+
+[pep8]
+
+max-line-length = 100
-- 
GitLab


From c6d65fa990db99779f862e579c6c37249a790283 Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Thu, 9 Aug 2018 13:23:17 +0200
Subject: [PATCH 2/9] Added typescript models

---
 core/serializers/student.py    |   2 +-
 core/urls.py                   |  23 +-
 core/views/common_views.py     |   9 +-
 frontend/src/api.ts            | 126 ++---
 frontend/src/models.ts         | 827 +++++++++++++++++++++++++++++++++
 frontend/tsconfig.json         |   2 +-
 grady/settings/default.py      |  13 +-
 swagger-api-specification.json |   1 +
 8 files changed, 934 insertions(+), 69 deletions(-)
 create mode 100644 frontend/src/models.ts
 create mode 100644 swagger-api-specification.json

diff --git a/core/serializers/student.py b/core/serializers/student.py
index 211069c3..32f7c379 100644
--- a/core/serializers/student.py
+++ b/core/serializers/student.py
@@ -23,7 +23,7 @@ class StudentInfoSerializer(DynamicFieldsModelSerializer):
                   'passes_exam')
 
 
-class StudentInfoSerializerForListView(DynamicFieldsModelSerializer):
+class StudentInfoForListViewSerializer(DynamicFieldsModelSerializer):
     name = serializers.ReadOnlyField(source='user.fullname')
     user = serializers.ReadOnlyField(source='user.username')
     user_pk = serializers.ReadOnlyField(source='user.pk')
diff --git a/core/urls.py b/core/urls.py
index 6772c660..3e3014d9 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -1,7 +1,11 @@
-from django.urls import path
+from django.urls import path, re_path
+from drf_yasg.views import get_schema_view
+from drf_yasg import openapi
 from rest_framework.routers import DefaultRouter
+from rest_framework.permissions import IsAdminUser, AllowAny
 
 from core import views
+from core.permissions import IsReviewer
 
 # Create a router and register our viewsets with it.
 router = DefaultRouter()
@@ -20,6 +24,17 @@ router.register('assignment', views.AssignmentApiViewSet)
 router.register('statistics', views.StatisticsEndpoint, base_name='statistics')
 router.register('user', views.UserAccountViewSet, base_name='user')
 
+schema_view = get_schema_view(
+    openapi.Info(
+        title="Grady API",
+        default_version='v1',
+        description="Blub",
+    ),
+    # validators=['flex', 'ssv'],
+    public=True,
+    permission_classes=(AllowAny,),
+)
+
 # regular views that are not viewsets
 regular_views_urlpatterns = [
     path('student-page/',
@@ -33,6 +48,12 @@ regular_views_urlpatterns = [
          views.get_jwt_expiration_delta,
          name='jwt-time-delta'),
     path('export/csv/', views.StudentCSVExport.as_view(), name='export-csv'),
+    re_path(r'swagger(?P<format>\.json|\.yaml)$',
+            schema_view.without_ui(cache_timeout=0), name='schema-json'),
+    re_path(r'swagger/$', schema_view.with_ui('swagger', cache_timeout=0),
+            name='schema-swagger-ui'),
+    re_path(r'redoc/$', schema_view.with_ui('redoc', cache_timeout=0),
+            name='schema-redoc'),
 ]
 
 urlpatterns = [
diff --git a/core/views/common_views.py b/core/views/common_views.py
index 231910e4..ea2fdccb 100644
--- a/core/views/common_views.py
+++ b/core/views/common_views.py
@@ -21,7 +21,7 @@ from core import models
 from core.models import ExamType, StudentInfo, SubmissionType
 from core.permissions import IsReviewer, IsStudent, IsTutorOrReviewer
 from core.serializers import (ExamSerializer, StudentInfoSerializer,
-                              StudentInfoSerializerForListView,
+                              StudentInfoForListViewSerializer,
                               SubmissionNoTypeSerializer, SubmissionSerializer,
                               SubmissionTypeSerializer, TutorSerializer,
                               UserAccountSerializer)
@@ -70,7 +70,7 @@ class StudentReviewerApiViewSet(viewsets.ReadOnlyModelViewSet):
         .prefetch_related('submissions__feedback')\
         .prefetch_related('submissions__type')\
         .all()
-    serializer_class = StudentInfoSerializerForListView
+    serializer_class = StudentInfoForListViewSerializer
 
     def _set_students_active(self, active):
         for student in self.get_queryset():
@@ -143,13 +143,14 @@ class StatisticsEndpoint(viewsets.ViewSet):
             models.Feedback.objects.aggregate(avg=Avg('score')).get('avg', 0),
 
             'submission_type_progress':
-                models.SubmissionType.get_annotated_feedback_count().values(
+                # Queryset is explicitly evaluated so camelizer plugin camelizes it
+                list(models.SubmissionType.get_annotated_feedback_count().values(
                     'pk',
                     'name',
                     'submission_count',
                     'feedback_final',
                     'feedback_in_validation',
-                    'feedback_in_conflict')
+                    'feedback_in_conflict'))
         })
 
 
diff --git a/frontend/src/api.ts b/frontend/src/api.ts
index 670854ca..57bf23bf 100644
--- a/frontend/src/api.ts
+++ b/frontend/src/api.ts
@@ -1,11 +1,23 @@
-import axios from 'axios'
+import axios, {AxiosInstance, AxiosPromise, AxiosResponse} from 'axios'
 import {Credentials} from '@/store/modules/authentication'
-
-function addFieldsToUrl ({url, fields = []}: {url: string, fields?: string[]}) {
+import {
+    Assignment,
+    Exam,
+    Feedback, FeedbackComment,
+    JSONWebToken, Statistics,
+    StudentInfo,
+    StudentInfoForListView,
+    Submission,
+    SubmissionNoType, SubmissionType,
+    Subscription,
+    Tutor, UserAccount
+} from "@/models";
+
+function addFieldsToUrl ({url, fields = []}: {url: string, fields?: string[]}): string {
   return fields.length > 0 ? url + '?fields=pk,' + fields : url
 }
 
-function getInstanceBaseUrl () {
+function getInstanceBaseUrl (): string {
   if (process.env.NODE_ENV === 'production') {
     return `https://${window.location.host}${window.location.pathname}`
   } else {
@@ -13,7 +25,7 @@ function getInstanceBaseUrl () {
   }
 }
 
-let ax = axios.create({
+let ax: AxiosInstance = axios.create({
   baseURL: getInstanceBaseUrl()
 })
 {
@@ -23,43 +35,39 @@ let ax = axios.create({
   }
 }
 
-export async function registerTutor (credentials: Credentials) {
-  return ax.post('/api/tutor/register/', credentials)
+export async function registerTutor (credentials: Credentials): Promise<AxiosResponse<Tutor>> {
+  return ax.post<Tutor>('/api/tutor/register/', credentials)
 }
 
-export async function fetchJWT (credentials: Credentials): Promise<string> {
+export async function fetchJWT (credentials: Credentials): Promise<JSONWebToken> {
   const token: string = (await ax.post('/api/get-token/', credentials)).data.token
   ax.defaults.headers['Authorization'] = `JWT ${token}`
-  return token
+  return {token}
 }
 
-export async function refreshJWT (token: string): Promise<string> {
-  const newToken: string = (await ax.post('/api/refresh-token/', {token})).data.token
-  ax.defaults.headers['Authorization'] = `JWT ${newToken}`
-  return newToken
+export async function refreshJWT (oldToken: string): Promise<JSONWebToken> {
+  const token: string = (await ax.post('/api/refresh-oldToken/', {oldToken})).data.token
+  ax.defaults.headers['Authorization'] = `JWT ${token}`
+  return {token}
 }
 
 export async function fetchJWTTimeDelta (): Promise<number> {
   return (await ax.get('/api/jwt-time-delta/')).data.timeDelta
 }
 
-export async function fetchUserRole (): Promise<string> {
-  return (await ax.get('/api/user-role/')).data.role
-}
-
-export async function fetchStudentSelfData () {
+export async function fetchStudentSelfData (): Promise<StudentInfo> {
   return (await ax.get('/api/student-page/')).data
 }
 
-export async function fetchStudentSubmissions () {
+export async function fetchStudentSubmissions (): Promise<Array<Submission>> {
   return (await ax.get('/api/student-submissions/')).data
 }
 
-export async function fetchSubmissionFeedbackTests ({pk}: {pk: string}) {
+export async function fetchSubmissionFeedbackTests ({pk}: {pk: string}): Promise<SubmissionNoType> {
   return (await ax.get(`/api/submission/${pk}/`)).data
 }
 
-export async function fetchAllStudents (fields: string[] = []) {
+export async function fetchAllStudents (fields: string[] = []): Promise<Array<StudentInfoForListView>> {
   const url = addFieldsToUrl({
     url: '/api/student/',
     fields
@@ -68,7 +76,7 @@ export async function fetchAllStudents (fields: string[] = []) {
 }
 
 export async function fetchStudent ({pk, fields = []}:
-{pk: string, fields?: string[]}) {
+{pk: string, fields?: string[]}): Promise<StudentInfoForListView> {
   const url = addFieldsToUrl({
     url: `/api/student/${pk}/`,
     fields
@@ -76,7 +84,7 @@ export async function fetchStudent ({pk, fields = []}:
   return (await ax.get(url)).data
 }
 
-export async function fetchAllTutors (fields: string[] = []) {
+export async function fetchAllTutors (fields: string[] = []): Promise<Array<Tutor>> {
   const url = addFieldsToUrl({
     url: '/api/tutor/',
     fields
@@ -84,20 +92,20 @@ export async function fetchAllTutors (fields: string[] = []) {
   return (await ax.get(url)).data
 }
 
-export async function fetchSubscriptions () {
+export async function fetchSubscriptions (): Promise<Array<Subscription>> {
   return (await ax.get('/api/subscription/')).data
 }
 
-export async function deactivateSubscription ({pk}: {pk: string}) {
+export async function deactivateSubscription ({pk}: {pk: string}): Promise<AxiosResponse<void>> {
   const url = `/api/subscription/${pk}/`
-  return (await ax.delete(url)).data
+  return ax.delete(url)
 }
 
-export async function fetchSubscription (subscriptionPk: string) {
+export async function fetchSubscription (subscriptionPk: string): Promise<Subscription> {
   return (await ax.get(`/api/subscription/${subscriptionPk}/`)).data
 }
 
-export async function fetchAllFeedback (fields: string[] = []) {
+export async function fetchAllFeedback (fields: string[] = []): Promise<Array<Feedback>> {
   const url = addFieldsToUrl({
     url: '/api/feedback/',
     fields
@@ -105,20 +113,20 @@ export async function fetchAllFeedback (fields: string[] = []) {
   return (await ax.get(url)).data
 }
 
-export async function fetchFeedback ({ofSubmission}) {
+export async function fetchFeedback ({ofSubmission}: {ofSubmission: string}): Promise<Feedback> {
   const url = `/api/feedback/${ofSubmission}/`
   return (await ax.get(url)).data
 }
 
 export async function fetchExamType ({examPk, fields = []}:
-{examPk?: string, fields?: string[]}) {
+{examPk?: string, fields?: string[]}): Promise<Exam | Array<Exam>> {
   const url = addFieldsToUrl({
     url: `/api/examtype/${examPk !== undefined ? examPk + '/' : ''}`,
     fields})
   return (await ax.get(url)).data
 }
 
-export async function fetchStatistics (opt = {fields: []}) {
+export async function fetchStatistics (opt = {fields: []}): Promise<Statistics> {
   const url = addFieldsToUrl({
     url: '/api/statistics/',
     fields: opt.fields
@@ -126,45 +134,41 @@ export async function fetchStatistics (opt = {fields: []}) {
   return (await ax.get(url)).data
 }
 
-interface SubscriptionPayload {
-  /* eslint-disable */
-  query_type: string,
-  query_key?: string,
-  feedback_stage?: string
-  /* eslint-enable */
-}
-
-export async function subscribeTo (type: string, key: string, stage: string) {
-  let data: SubscriptionPayload = {
-    query_type: type
+export async function subscribeTo (type: Subscription.QueryTypeEnum,
+                                   key: string,
+                                   stage: Subscription.FeedbackStageEnum): Promise<Subscription> {
+  let data: Subscription = {
+    queryType: type
   }
 
   if (key) {
-    data.query_key = key
+    data.queryKey = key
   }
   if (stage) {
-    data.feedback_stage = stage
+    data.feedbackStage = stage
   }
 
   return (await ax.post('/api/subscription/', data)).data
 }
 
-export async function createAssignment ({subscription = null, subscriptionPk = ''}) {
+export async function createAssignment (
+    {subscription = undefined, subscriptionPk = ''}:
+        {subscription?: Subscription, subscriptionPk?: string}): Promise<Assignment> {
   const data = {
     subscription: subscription ? subscription.pk : subscriptionPk
   }
   return (await ax.post(`/api/assignment/`, data)).data
 }
 
-export async function submitFeedbackForAssignment ({feedback}) {
+export async function submitFeedbackForAssignment ({feedback}: {feedback: Feedback}): Promise<Feedback> {
   return (await ax.post('/api/feedback/', feedback)).data
 }
 
-export async function submitUpdatedFeedback ({feedback}) {
-  return (await ax.patch(`/api/feedback/${feedback.of_submission}/`, feedback)).data
+export async function submitUpdatedFeedback ({feedback}: {feedback: Feedback}): Promise<Feedback> {
+  return (await ax.patch(`/api/feedback/${feedback.ofSubmission}/`, feedback)).data
 }
 
-export async function fetchSubmissionTypes (fields = []) {
+export async function fetchSubmissionTypes (fields = []): Promise<Array<SubmissionType>> {
   let url = '/api/submissiontype/'
   if (fields.length > 0) {
     url += '?fields=pk,' + fields
@@ -172,43 +176,43 @@ export async function fetchSubmissionTypes (fields = []) {
   return (await ax.get(url)).data
 }
 
-export async function fetchAllAssignments (fields = []) {
+export async function fetchAllAssignments (fields = []): Promise<Array<Assignment>> {
   const url = addFieldsToUrl({url: '/api/assignment/', fields})
   return (await ax.get(url)).data
 }
 
-export async function deleteAssignment ({assignment}) {
+export async function deleteAssignment ({assignment}: {assignment: Assignment}): Promise<AxiosResponse<void>> {
   const url = `/api/assignment/${assignment.pk}/`
-  return (await ax.delete(url)).data
+  return ax.delete(url)
 }
 
-export async function deleteComment (comment = {pk: undefined}) {
+export async function deleteComment (comment = {pk: undefined}): Promise<AxiosResponse<void>> {
   const url = `/api/feedback-comment/${comment.pk}/`
-  return (await ax.delete(url)).data
+  return await ax.delete(url)
 }
 
-export async function patchComment (comment = {pk: undefined}) {
+export async function patchComment (comment = {pk: undefined}): Promise<FeedbackComment> {
   const url = `/api/feedback-comment/${comment.pk}/`
   return (await ax.patch(url, comment)).data
 }
 
-export async function activateAllStudentAccess () {
+export async function activateAllStudentAccess (): Promise<AxiosResponse<void>> {
   return ax.post('/api/student/activate/')
 }
 
-export async function deactivateAllStudentAccess () {
+export async function deactivateAllStudentAccess (): Promise<AxiosResponse<void>> {
   return ax.post('/api/student/deactivate/')
 }
 
-export async function changePassword (userPk: string, data) {
-  return ax.patch(`/api/user/${userPk}/change_password/`, data)
+export async function changePassword (userPk: string, data: {password: string}): Promise<UserAccount> {
+  return (await ax.patch(`/api/user/${userPk}/change_password/`, data)).data
 }
 
-export async function getOwnUser () {
+export async function getOwnUser (): Promise<UserAccount> {
   return (await ax.get('/api/user/me/')).data
 }
 
-export async function changeActiveForUser (userPk: string, active: boolean) {
+export async function changeActiveForUser (userPk: string, active: boolean): Promise<UserAccount> {
   return (await ax.patch(`/api/user/${userPk}/change_active/`, {'is_active': active})).data
 }
 
diff --git a/frontend/src/models.ts b/frontend/src/models.ts
new file mode 100644
index 00000000..485884b2
--- /dev/null
+++ b/frontend/src/models.ts
@@ -0,0 +1,827 @@
+/**
+ *
+ * @export
+ * @interface Assignment
+ */
+export interface Assignment {
+    /**
+     *
+     * @type {string}
+     * @memberof Assignment
+     */
+    pk?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Assignment
+     */
+    submission?: string;
+    /**
+     *
+     * @type {boolean}
+     * @memberof Assignment
+     */
+    isDone?: boolean;
+    /**
+     *
+     * @type {string}
+     * @memberof Assignment
+     */
+    owner?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Assignment
+     */
+    stage?: string;
+}
+
+/**
+ *
+ * @export
+ * @interface Exam
+ */
+export interface Exam {
+    /**
+     *
+     * @type {string}
+     * @memberof Exam
+     */
+    pk?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Exam
+     */
+    moduleReference: string;
+    /**
+     *
+     * @type {number}
+     * @memberof Exam
+     */
+    totalScore: number;
+    /**
+     *
+     * @type {number}
+     * @memberof Exam
+     */
+    passScore: number;
+    /**
+     *
+     * @type {boolean}
+     * @memberof Exam
+     */
+    passOnly?: boolean;
+}
+
+/**
+ *
+ * @export
+ * @interface Feedback
+ */
+export interface Feedback {
+    /**
+     *
+     * @type {number}
+     * @memberof Feedback
+     */
+    pk?: number;
+    /**
+     *
+     * @type {string}
+     * @memberof Feedback
+     */
+    ofSubmission: string;
+    /**
+     *
+     * @type {boolean}
+     * @memberof Feedback
+     */
+    isFinal?: boolean;
+    /**
+     *
+     * @type {number}
+     * @memberof Feedback
+     */
+    score?: number;
+    /**
+     *
+     * @type {Array<FeedbackComment>}
+     * @memberof Feedback
+     */
+    feedbackLines?: {[lineNo: number]: FeedbackComment[]};
+    /**
+     *
+     * @type {Date}
+     * @memberof Feedback
+     */
+    created?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Feedback
+     */
+    ofSubmissionType?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Feedback
+     */
+    feedbackStageForUser?: string;
+}
+
+/**
+ *
+ * @export
+ * @interface FeedbackComment
+ */
+export interface FeedbackComment {
+    /**
+     *
+     * @type {string}
+     * @memberof FeedbackComment
+     */
+    pk?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof FeedbackComment
+     */
+    text: string;
+    /**
+     *
+     * @type {Date}
+     * @memberof FeedbackComment
+     */
+    created?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof FeedbackComment
+     */
+    ofTutor?: string;
+    /**
+     *
+     * @type {number}
+     * @memberof FeedbackComment
+     */
+    ofLine?: number;
+    /**
+     *
+     * @type {boolean}
+     * @memberof FeedbackComment
+     */
+    visibleToStudent?: boolean;
+}
+
+/**
+ *
+ * @export
+ * @interface Credentials
+ */
+export interface Credentials {
+    /**
+     *
+     * @type {string}
+     * @memberof JSONWebToken
+     */
+    username: string;
+    /**
+     *
+     * @type {string}
+     * @memberof JSONWebToken
+     */
+    password: string;
+}
+
+
+export interface JSONWebToken {
+    token: string
+}
+
+export interface Statistics {
+    submissionsPerType: number,
+    submissionsPerStudent: number,
+    currentMeanScore: number,
+    submissionTypeProgress: Array<SubmissionTypeProgress>
+}
+
+/**
+ *
+ * @export
+ * @interface StudentInfo
+ */
+export interface StudentInfo {
+    /**
+     *
+     * @type {string}
+     * @memberof StudentInfo
+     */
+    pk?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof StudentInfo
+     */
+    name?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof StudentInfo
+     */
+    user: string;
+    /**
+     *
+     * @type {string}
+     * @memberof StudentInfo
+     */
+    matrikelNo?: string;
+    /**
+     *
+     * @type {Exam}
+     * @memberof StudentInfo
+     */
+    exam: Exam;
+    /**
+     *
+     * @type {Array<SubmissionList>}
+     * @memberof StudentInfo
+     */
+    submissions: Array<SubmissionList>;
+    /**
+     *
+     * @type {boolean}
+     * @memberof StudentInfo
+     */
+    passesExam?: boolean;
+}
+
+/**
+ *
+ * @export
+ * @interface StudentInfoForListView
+ */
+export interface StudentInfoForListView {
+    /**
+     *
+     * @type {string}
+     * @memberof StudentInfoForListView
+     */
+    pk?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof StudentInfoForListView
+     */
+    name?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof StudentInfoForListView
+     */
+    user?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof StudentInfoForListView
+     */
+    userPk?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof StudentInfoForListView
+     */
+    exam?: string;
+    /**
+     *
+     * @type {Array<SubmissionNoTextFields>}
+     * @memberof StudentInfoForListView
+     */
+    submissions: Array<SubmissionNoTextFields>;
+    /**
+     *
+     * @type {string}
+     * @memberof StudentInfoForListView
+     */
+    matrikelNo?: string;
+    /**
+     *
+     * @type {boolean}
+     * @memberof StudentInfoForListView
+     */
+    passesExam?: boolean;
+    /**
+     *
+     * @type {boolean}
+     * @memberof StudentInfoForListView
+     */
+    isActive: boolean;
+}
+
+/**
+ *
+ * @export
+ * @interface Submission
+ */
+export interface Submission {
+    /**
+     *
+     * @type {string}
+     * @memberof Submission
+     */
+    pk?: string;
+    /**
+     *
+     * @type {SubmissionType}
+     * @memberof Submission
+     */
+    type: SubmissionType;
+    /**
+     *
+     * @type {string}
+     * @memberof Submission
+     */
+    text?: string;
+    /**
+     *
+     * @type {VisibleCommentFeedback}
+     * @memberof Submission
+     */
+    feedback: VisibleCommentFeedback;
+    /**
+     *
+     * @type {Array<Test>}
+     * @memberof Submission
+     */
+    tests: Array<Test>;
+}
+
+/**
+ *
+ * @export
+ * @interface SubmissionList
+ */
+export interface SubmissionList {
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionList
+     */
+    pk?: string;
+    /**
+     *
+     * @type {SubmissionTypeList}
+     * @memberof SubmissionList
+     */
+    type: SubmissionTypeList;
+    /**
+     *
+     * @type {Feedback}
+     * @memberof SubmissionList
+     */
+    feedback: Feedback;
+}
+
+/**
+ *
+ * @export
+ * @interface SubmissionNoTextFields
+ */
+export interface SubmissionNoTextFields {
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionNoTextFields
+     */
+    pk?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionNoTextFields
+     */
+    type: string;
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionNoTextFields
+     */
+    score?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionNoTextFields
+     */
+    _final?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionNoTextFields
+     */
+    fullScore?: string;
+}
+
+/**
+ *
+ * @export
+ * @interface SubmissionNoType
+ */
+export interface SubmissionNoType {
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionNoType
+     */
+    pk?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionNoType
+     */
+    type: string;
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionNoType
+     */
+    fullScore?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionNoType
+     */
+    text?: string;
+    /**
+     *
+     * @type {Feedback}
+     * @memberof SubmissionNoType
+     */
+    feedback: Feedback;
+    /**
+     *
+     * @type {Array<Test>}
+     * @memberof SubmissionNoType
+     */
+    tests: Array<Test>;
+}
+
+/**
+ *
+ * @export
+ * @interface SubmissionType
+ */
+export interface SubmissionType {
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionType
+     */
+    pk?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionType
+     */
+    name: string;
+    /**
+     *
+     * @type {number}
+     * @memberof SubmissionType
+     */
+    fullScore?: number;
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionType
+     */
+    description: string;
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionType
+     */
+    solution: string;
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionType
+     */
+    programmingLanguage?: SubmissionType.ProgrammingLanguageEnum;
+}
+
+/**
+ * @export
+ * @namespace SubmissionType
+ */
+export namespace SubmissionType {
+    /**
+     * @export
+     * @enum {string}
+     */
+    export enum ProgrammingLanguageEnum {
+        C = <any> 'c',
+        Java = <any> 'java'
+    }
+}
+
+/**
+ *
+ * @export
+ * @interface SubmissionTypeList
+ */
+export interface SubmissionTypeList {
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionTypeList
+     */
+    pk?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof SubmissionTypeList
+     */
+    name: string;
+    /**
+     *
+     * @type {number}
+     * @memberof SubmissionTypeList
+     */
+    fullScore?: number;
+}
+
+export interface SubmissionTypeProgress {
+    pk?: string,
+    name: string,
+    feedbackFinal: number,
+    feedbackInValidation: number,
+    feedbackInConflict: number,
+    submissionCount: number
+}
+
+/**
+ *
+ * @export
+ * @interface Subscription
+ */
+export interface Subscription {
+    /**
+     *
+     * @type {string}
+     * @memberof Subscription
+     */
+    pk?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Subscription
+     */
+    owner?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Subscription
+     */
+    queryType?: Subscription.QueryTypeEnum;
+    /**
+     *
+     * @type {string}
+     * @memberof Subscription
+     */
+    queryKey?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Subscription
+     */
+    feedbackStage?: Subscription.FeedbackStageEnum;
+    /**
+     *
+     * @type {boolean}
+     * @memberof Subscription
+     */
+    deactivated?: boolean;
+    /**
+     *
+     * @type {string}
+     * @memberof Subscription
+     */
+    assignments?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Subscription
+     */
+    remaining?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Subscription
+     */
+    available?: string;
+}
+
+/**
+ * @export
+ * @namespace Subscription
+ */
+export namespace Subscription {
+    /**
+     * @export
+     * @enum {string}
+     */
+    export enum QueryTypeEnum {
+        Random = <any> 'random',
+        Student = <any> 'student',
+        Exam = <any> 'exam',
+        SubmissionType = <any> 'submission_type'
+    }
+    /**
+     * @export
+     * @enum {string}
+     */
+    export enum FeedbackStageEnum {
+        Creation = <any> 'feedback-creation',
+        Validation = <any> 'feedback-validation',
+        ConflictResolution = <any> 'feedback-conflict-resolution'
+    }
+}
+
+/**
+ *
+ * @export
+ * @interface Test
+ */
+export interface Test {
+    /**
+     *
+     * @type {string}
+     * @memberof Test
+     */
+    pk?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Test
+     */
+    name: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Test
+     */
+    label: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Test
+     */
+    annotation: string;
+}
+
+/**
+ *
+ * @export
+ * @interface Tutor
+ */
+export interface Tutor {
+    /**
+     *
+     * @type {string}
+     * @memberof Tutor
+     */
+    pk?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Tutor
+     */
+    password?: string;
+    /**
+     * Designates whether this user should be treated as active. Unselect this instead of deleting accounts.
+     * @type {boolean}
+     * @memberof Tutor
+     */
+    isActive?: boolean;
+    /**
+     * Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.
+     * @type {string}
+     * @memberof Tutor
+     */
+    username: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Tutor
+     */
+    feedbackCreated?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof Tutor
+     */
+    feedbackValidated?: string;
+}
+
+/**
+ *
+ * @export
+ * @interface UserAccount
+ */
+export interface UserAccount {
+    /**
+     *
+     * @type {string}
+     * @memberof UserAccount
+     */
+    pk?: string;
+    /**
+     * Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.
+     * @type {string}
+     * @memberof UserAccount
+     */
+    username?: string;
+    /**
+     *
+     * @type {string}
+     * @memberof UserAccount
+     */
+    role?: UserAccount.RoleEnum;
+    /**
+     *
+     * @type {boolean}
+     * @memberof UserAccount
+     */
+    isAdmin?: boolean;
+    /**
+     *
+     * @type {string}
+     * @memberof UserAccount
+     */
+    password?: string;
+}
+
+/**
+ * @export
+ * @namespace UserAccount
+ */
+export namespace UserAccount {
+    /**
+     * @export
+     * @enum {string}
+     */
+    export enum RoleEnum {
+        Student = <any> 'Student',
+        Tutor = <any> 'Tutor',
+        Reviewer = <any> 'Reviewer'
+    }
+}
+
+/**
+ *
+ * @export
+ * @interface VisibleCommentFeedback
+ */
+export interface VisibleCommentFeedback {
+    /**
+     *
+     * @type {number}
+     * @memberof VisibleCommentFeedback
+     */
+    pk?: number;
+    /**
+     *
+     * @type {string}
+     * @memberof VisibleCommentFeedback
+     */
+    ofSubmission: string;
+    /**
+     *
+     * @type {boolean}
+     * @memberof VisibleCommentFeedback
+     */
+    isFinal?: boolean;
+    /**
+     *
+     * @type {number}
+     * @memberof VisibleCommentFeedback
+     */
+    score?: number;
+    /**
+     *
+     * @type {string}
+     * @memberof VisibleCommentFeedback
+     */
+    feedbackLines?: string;
+    /**
+     *
+     * @type {Date}
+     * @memberof VisibleCommentFeedback
+     */
+    created?: Date;
+    /**
+     *
+     * @type {string}
+     * @memberof VisibleCommentFeedback
+     */
+    ofSubmissionType?: string;
+}
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
index c800f3ea..59a075aa 100644
--- a/frontend/tsconfig.json
+++ b/frontend/tsconfig.json
@@ -2,7 +2,7 @@
   "compilerOptions": {
     "target": "esnext",
     "module": "esnext",
-    "strict": false,
+    "strict": true,
     "jsx": "preserve",
     "importHelpers": true,
     "moduleResolution": "node",
diff --git a/grady/settings/default.py b/grady/settings/default.py
index 1ff12749..a9a9fbee 100644
--- a/grady/settings/default.py
+++ b/grady/settings/default.py
@@ -43,6 +43,7 @@ INSTALLED_APPS = [
     'rest_framework',
     'corsheaders',
     'drf_dynamic_fields',
+    'drf_yasg',
     'core',
 ]
 
@@ -138,7 +139,17 @@ REST_FRAMEWORK = {
         'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
         'rest_framework.authentication.SessionAuthentication',
         'rest_framework.authentication.BasicAuthentication',
-    )
+    ),
+    'DEFAULT_RENDERER_CLASSES': (
+        'djangorestframework_camel_case.render.CamelCaseJSONRenderer',
+    ),
+
+    'DEFAULT_PARSER_CLASSES': (
+        'djangorestframework_camel_case.parser.CamelCaseJSONParser',
+    ),
+    # 'JSON_UNDERSCOREIZE': {
+    #     'no_underscore_before_number': True,
+    # },
 }
 
 JWT_AUTH = {
diff --git a/swagger-api-specification.json b/swagger-api-specification.json
new file mode 100644
index 00000000..29c9f7d3
--- /dev/null
+++ b/swagger-api-specification.json
@@ -0,0 +1 @@
+{"swagger": "2.0", "info": {"title": "Grady API", "description": "Blub", "version": "v1"}, "host": "localhost:8000", "schemes": ["http"], "basePath": "/api", "consumes": ["application/json"], "produces": ["application/json"], "securityDefinitions": {"basic": {"type": "basic"}}, "security": [{"basic": []}], "paths": {"/assignment/": {"get": {"operationId": "assignment_list", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/Assignment"}}}}, "tags": ["assignment"]}, "post": {"operationId": "assignment_create", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Assignment"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/Assignment"}}}, "tags": ["assignment"]}, "parameters": []}, "/assignment/{assignment_id}/": {"get": {"operationId": "assignment_read", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Assignment"}}}, "tags": ["assignment"]}, "delete": {"operationId": "assignment_delete", "description": "Stop working on the assignment before it is finished", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["assignment"]}, "parameters": [{"name": "assignment_id", "in": "path", "description": "A UUID string identifying this tutor submission assignment.", "required": true, "type": "string", "format": "uuid"}]}, "/examtype/": {"get": {"operationId": "examtype_list", "description": "Gets a list of an individual exam by Id if provided", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/Exam"}}}}, "tags": ["examtype"]}, "parameters": []}, "/examtype/{exam_type_id}/": {"get": {"operationId": "examtype_read", "description": "Gets a list of an individual exam by Id if provided", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Exam"}}}, "tags": ["examtype"]}, "parameters": [{"name": "exam_type_id", "in": "path", "description": "A UUID string identifying this ExamType.", "required": true, "type": "string", "format": "uuid"}]}, "/export/csv/": {"get": {"operationId": "export_csv_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "produces": ["text/csv"], "tags": ["export"]}, "parameters": []}, "/feedback-comment/{comment_id}/": {"patch": {"operationId": "feedback-comment_partial_update", "description": "Gets a list of an individual exam by Id if provided", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/FeedbackComment"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/FeedbackComment"}}}, "tags": ["feedback-comment"]}, "delete": {"operationId": "feedback-comment_delete", "description": "Gets a list of an individual exam by Id if provided", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["feedback-comment"]}, "parameters": [{"name": "comment_id", "in": "path", "description": "A UUID string identifying this Feedback Comment.", "required": true, "type": "string", "format": "uuid"}]}, "/feedback/": {"get": {"operationId": "feedback_list", "description": "Gets a list of an individual exam by Id if provided", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/Feedback"}}}}, "tags": ["feedback"]}, "post": {"operationId": "feedback_create", "description": "Gets a list of an individual exam by Id if provided", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Feedback"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/Feedback"}}}, "tags": ["feedback"]}, "parameters": []}, "/feedback/{submission_pk}/": {"get": {"operationId": "feedback_read", "description": "Gets a list of an individual exam by Id if provided", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Feedback"}}}, "tags": ["feedback"]}, "patch": {"operationId": "feedback_partial_update", "description": "Gets a list of an individual exam by Id if provided", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Feedback"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Feedback"}}}, "tags": ["feedback"]}, "parameters": [{"name": "submission_pk", "in": "path", "required": true, "type": "string"}]}, "/get-token/": {"post": {"operationId": "get-token_create", "description": "API View that receives a POST with a user's username and password.\n\nReturns a JSON Web Token that can be used for authenticated requests.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/JSONWebToken"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/JSONWebToken"}}}, "tags": ["get-token"]}, "parameters": []}, "/jwt-time-delta/": {"get": {"operationId": "jwt-time-delta_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["jwt-time-delta"]}, "parameters": []}, "/refresh-token/": {"post": {"operationId": "refresh-token_create", "description": "API View that returns a refreshed token (with new expiration) based on\nexisting token\n\nIf 'orig_iat' field (original issued-at-time) is found, will first check\nif it's within expiration window, then copy it to the new token", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/RefreshJSONWebToken"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/RefreshJSONWebToken"}}}, "tags": ["refresh-token"]}, "parameters": []}, "/statistics/": {"get": {"operationId": "statistics_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["statistics"]}, "parameters": []}, "/student-page/": {"get": {"operationId": "student-page_read", "description": "Gets all data that belongs to one student", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/StudentInfo"}}}, "tags": ["student-page"]}, "parameters": []}, "/student-submissions/": {"get": {"operationId": "student-submissions_list", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/Submission"}}}}, "tags": ["student-submissions"]}, "parameters": []}, "/student/": {"get": {"operationId": "student_list", "description": "Gets a list of all students without individual submissions", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/StudentInfoForListView"}}}}, "tags": ["student"]}, "parameters": []}, "/student/activate/": {"post": {"operationId": "student_activate", "description": "Gets a list of all students without individual submissions", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/StudentInfoForListView"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/StudentInfoForListView"}}}, "tags": ["student"]}, "parameters": []}, "/student/deactivate/": {"post": {"operationId": "student_deactivate", "description": "Gets a list of all students without individual submissions", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/StudentInfoForListView"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/StudentInfoForListView"}}}, "tags": ["student"]}, "parameters": []}, "/student/{student_id}/": {"get": {"operationId": "student_read", "description": "Gets a list of all students without individual submissions", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/StudentInfoForListView"}}}, "tags": ["student"]}, "parameters": [{"name": "student_id", "in": "path", "description": "A UUID string identifying this Student.", "required": true, "type": "string", "format": "uuid"}]}, "/submission/": {"get": {"operationId": "submission_list", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/SubmissionNoType"}}}}, "tags": ["submission"]}, "parameters": []}, "/submission/{submission_id}/": {"get": {"operationId": "submission_read", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/SubmissionNoType"}}}, "tags": ["submission"]}, "parameters": [{"name": "submission_id", "in": "path", "description": "A UUID string identifying this Submission.", "required": true, "type": "string", "format": "uuid"}]}, "/submissiontype/": {"get": {"operationId": "submissiontype_list", "description": "Gets a list or a detail view of a single SubmissionType", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/SubmissionType"}}}}, "tags": ["submissiontype"]}, "parameters": []}, "/submissiontype/{submission_type_id}/": {"get": {"operationId": "submissiontype_read", "description": "Gets a list or a detail view of a single SubmissionType", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/SubmissionType"}}}, "tags": ["submissiontype"]}, "parameters": [{"name": "submission_type_id", "in": "path", "description": "A UUID string identifying this SubmissionType.", "required": true, "type": "string", "format": "uuid"}]}, "/subscription/": {"get": {"operationId": "subscription_list", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/Subscription"}}}}, "tags": ["subscription"]}, "post": {"operationId": "subscription_create", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Subscription"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/Subscription"}}}, "tags": ["subscription"]}, "parameters": []}, "/subscription/{subscription_id}/": {"get": {"operationId": "subscription_read", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Subscription"}}}, "tags": ["subscription"]}, "delete": {"operationId": "subscription_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["subscription"]}, "parameters": [{"name": "subscription_id", "in": "path", "description": "A UUID string identifying this submission subscription.", "required": true, "type": "string", "format": "uuid"}]}, "/tutor/": {"get": {"operationId": "tutor_list", "description": "Api endpoint for creating, listing, viewing or deleting tutors", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/Tutor"}}}}, "tags": ["tutor"]}, "post": {"operationId": "tutor_create", "description": "Api endpoint for creating, listing, viewing or deleting tutors", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Tutor"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/Tutor"}}}, "tags": ["tutor"]}, "parameters": []}, "/tutor/register/": {"post": {"operationId": "tutor_register", "description": "Api endpoint for creating, listing, viewing or deleting tutors", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Tutor"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/Tutor"}}}, "tags": ["tutor"]}, "parameters": []}, "/tutor/{user_id}/": {"get": {"operationId": "tutor_read", "description": "Api endpoint for creating, listing, viewing or deleting tutors", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Tutor"}}}, "tags": ["tutor"]}, "put": {"operationId": "tutor_update", "description": "Api endpoint for creating, listing, viewing or deleting tutors", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Tutor"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Tutor"}}}, "tags": ["tutor"]}, "patch": {"operationId": "tutor_partial_update", "description": "Api endpoint for creating, listing, viewing or deleting tutors", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Tutor"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Tutor"}}}, "tags": ["tutor"]}, "delete": {"operationId": "tutor_delete", "description": "Api endpoint for creating, listing, viewing or deleting tutors", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["tutor"]}, "parameters": [{"name": "user_id", "in": "path", "description": "A UUID string identifying this user.", "required": true, "type": "string", "format": "uuid"}]}, "/user-role/": {"get": {"operationId": "user-role_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["user-role"]}, "parameters": []}, "/user/": {"get": {"operationId": "user_list", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/UserAccount"}}}}, "tags": ["user"]}, "parameters": []}, "/user/me/": {"get": {"operationId": "user_me", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/UserAccount"}}}}, "tags": ["user"]}, "parameters": []}, "/user/{user_id}/": {"get": {"operationId": "user_read", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/UserAccount"}}}, "tags": ["user"]}, "parameters": [{"name": "user_id", "in": "path", "description": "A UUID string identifying this user.", "required": true, "type": "string", "format": "uuid"}]}, "/user/{user_id}/change_active/": {"patch": {"operationId": "user_change_active", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/UserAccount"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/UserAccount"}}}, "tags": ["user"]}, "parameters": [{"name": "user_id", "in": "path", "description": "A UUID string identifying this user.", "required": true, "type": "string", "format": "uuid"}]}, "/user/{user_id}/change_password/": {"patch": {"operationId": "user_change_password", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/UserAccount"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/UserAccount"}}}, "tags": ["user"]}, "parameters": [{"name": "user_id", "in": "path", "description": "A UUID string identifying this user.", "required": true, "type": "string", "format": "uuid"}]}}, "definitions": {"Assignment": {"type": "object", "properties": {"pk": {"title": "Assignment id", "type": "string", "format": "uuid", "readOnly": true}, "submission": {"title": "Submission", "type": "string", "format": "uuid", "readOnly": true}, "isDone": {"title": "Is done", "type": "boolean", "readOnly": true}, "owner": {"title": "Owner", "type": "string", "readOnly": true}, "stage": {"title": "Stage", "type": "string", "readOnly": true}}}, "Exam": {"required": ["moduleReference", "totalScore", "passScore"], "type": "object", "properties": {"pk": {"title": "Exam type id", "type": "string", "format": "uuid", "readOnly": true}, "moduleReference": {"title": "Module reference", "type": "string", "maxLength": 50, "minLength": 1}, "totalScore": {"title": "Total score", "type": "integer", "maximum": 2147483647, "minimum": 0}, "passScore": {"title": "Pass score", "type": "integer", "maximum": 2147483647, "minimum": 0}, "passOnly": {"title": "Pass only", "type": "boolean"}}}, "FeedbackComment": {"required": ["text"], "type": "object", "properties": {"pk": {"title": "Comment id", "type": "string", "format": "uuid", "readOnly": true}, "text": {"title": "Text", "type": "string", "minLength": 1}, "created": {"title": "Created", "type": "string", "format": "date-time", "readOnly": true}, "ofTutor": {"title": "Of tutor", "type": "string", "readOnly": true}, "ofLine": {"title": "Of line", "type": "integer", "maximum": 2147483647, "minimum": 0}, "visibleToStudent": {"title": "Visible to student", "type": "boolean"}}}, "Feedback": {"required": ["ofSubmission"], "type": "object", "properties": {"pk": {"title": "ID", "type": "integer", "readOnly": true}, "ofSubmission": {"title": "Of submission", "type": "string", "format": "uuid"}, "isFinal": {"title": "Is final", "type": "boolean"}, "score": {"title": "Score", "type": "integer", "maximum": 2147483647, "minimum": 0}, "feedbackLines": {"type": "array", "items": {"$ref": "#/definitions/FeedbackComment"}}, "created": {"title": "Created", "type": "string", "format": "date-time", "readOnly": true}, "ofSubmissionType": {"title": "Of submission type", "type": "string", "readOnly": true}, "feedbackStageForUser": {"title": "Feedback stage for user", "type": "string", "readOnly": true}}}, "JSONWebToken": {"required": ["username", "password"], "type": "object", "properties": {"username": {"title": "Username", "type": "string", "minLength": 1}, "password": {"title": "Password", "type": "string", "minLength": 1}}}, "RefreshJSONWebToken": {"required": ["token"], "type": "object", "properties": {"token": {"title": "Token", "type": "string", "minLength": 1}}}, "SubmissionTypeList": {"title": "Type", "required": ["name"], "type": "object", "properties": {"pk": {"title": "Submission type id", "type": "string", "format": "uuid", "readOnly": true}, "name": {"title": "Name", "type": "string", "maxLength": 100, "minLength": 1}, "fullScore": {"title": "Full score", "type": "integer", "maximum": 2147483647, "minimum": 0}}}, "SubmissionList": {"required": ["type", "feedback"], "type": "object", "properties": {"pk": {"title": "Submission id", "type": "string", "format": "uuid", "readOnly": true}, "type": {"$ref": "#/definitions/SubmissionTypeList"}, "feedback": {"$ref": "#/definitions/Feedback"}}}, "StudentInfo": {"required": ["user", "exam", "submissions"], "type": "object", "properties": {"pk": {"title": "Student id", "type": "string", "format": "uuid", "readOnly": true}, "name": {"title": "Name", "type": "string", "readOnly": true}, "user": {"title": "User", "type": "string", "format": "uuid"}, "matrikelNo": {"title": "Matrikel no", "type": "string", "readOnly": true}, "exam": {"$ref": "#/definitions/Exam"}, "submissions": {"type": "array", "items": {"$ref": "#/definitions/SubmissionList"}}, "passesExam": {"title": "Passes exam", "type": "boolean"}}}, "SubmissionType": {"title": "Type", "required": ["name", "description", "solution"], "type": "object", "properties": {"pk": {"title": "Submission type id", "type": "string", "format": "uuid", "readOnly": true}, "name": {"title": "Name", "type": "string", "maxLength": 100, "minLength": 1}, "fullScore": {"title": "Full score", "type": "integer", "maximum": 2147483647, "minimum": 0}, "description": {"title": "Description", "type": "string", "minLength": 1}, "solution": {"title": "Solution", "type": "string", "minLength": 1}, "programmingLanguage": {"title": "Programming language", "type": "string", "enum": ["c", "java"]}}}, "VisibleCommentFeedback": {"title": "Feedback", "required": ["ofSubmission"], "type": "object", "properties": {"pk": {"title": "ID", "type": "integer", "readOnly": true}, "ofSubmission": {"title": "Of submission", "type": "string", "format": "uuid"}, "isFinal": {"title": "Is final", "type": "boolean"}, "score": {"title": "Score", "type": "integer", "maximum": 2147483647, "minimum": 0}, "feedbackLines": {"title": "Feedback lines", "type": "string", "readOnly": true}, "created": {"title": "Created", "type": "string", "format": "date-time", "readOnly": true}, "ofSubmissionType": {"title": "Of submission type", "type": "string", "readOnly": true}}}, "Test": {"required": ["name", "label", "annotation"], "type": "object", "properties": {"pk": {"title": "Test id", "type": "string", "format": "uuid", "readOnly": true}, "name": {"title": "Name", "type": "string", "maxLength": 30, "minLength": 1}, "label": {"title": "Label", "type": "string", "maxLength": 50, "minLength": 1}, "annotation": {"title": "Annotation", "type": "string", "minLength": 1}}}, "Submission": {"required": ["type", "feedback", "tests"], "type": "object", "properties": {"pk": {"title": "Submission id", "type": "string", "format": "uuid", "readOnly": true}, "type": {"$ref": "#/definitions/SubmissionType"}, "text": {"title": "Text", "type": "string"}, "feedback": {"$ref": "#/definitions/VisibleCommentFeedback"}, "tests": {"type": "array", "items": {"$ref": "#/definitions/Test"}}}}, "SubmissionNoTextFields": {"required": ["type"], "type": "object", "properties": {"pk": {"title": "Submission id", "type": "string", "format": "uuid", "readOnly": true}, "type": {"title": "Type", "type": "string", "format": "uuid"}, "score": {"title": "Score", "type": "string", "readOnly": true}, "final": {"title": "Final", "type": "string", "readOnly": true}, "fullScore": {"title": "Full score", "type": "string", "readOnly": true}}}, "StudentInfoForListView": {"required": ["submissions", "isActive"], "type": "object", "properties": {"pk": {"title": "Student id", "type": "string", "format": "uuid", "readOnly": true}, "name": {"title": "Name", "type": "string", "readOnly": true}, "user": {"title": "User", "type": "string", "readOnly": true}, "userPk": {"title": "User pk", "type": "string", "readOnly": true}, "exam": {"title": "Exam", "type": "string", "readOnly": true}, "submissions": {"type": "array", "items": {"$ref": "#/definitions/SubmissionNoTextFields"}}, "matrikelNo": {"title": "Matrikel no", "type": "string", "maxLength": 30, "minLength": 1}, "passesExam": {"title": "Passes exam", "type": "boolean"}, "isActive": {"title": "Is active", "type": "boolean"}}}, "SubmissionNoType": {"required": ["type", "feedback", "tests"], "type": "object", "properties": {"pk": {"title": "Submission id", "type": "string", "format": "uuid", "readOnly": true}, "type": {"title": "Type", "type": "string", "format": "uuid"}, "fullScore": {"title": "Full score", "type": "string", "readOnly": true}, "text": {"title": "Text", "type": "string"}, "feedback": {"$ref": "#/definitions/Feedback"}, "tests": {"type": "array", "items": {"$ref": "#/definitions/Test"}}}}, "Subscription": {"type": "object", "properties": {"pk": {"title": "Subscription id", "type": "string", "format": "uuid", "readOnly": true}, "owner": {"title": "Owner", "type": "string", "readOnly": true}, "queryType": {"title": "Query type", "type": "string", "enum": ["random", "student", "exam", "submission_type"], "default": "random"}, "queryKey": {"title": "Query key", "type": "string", "format": "uuid"}, "feedbackStage": {"title": "Feedback stage", "type": "string", "enum": ["feedback-creation", "feedback-validation", "feedback-conflict-resolution"], "default": "feedback-creation"}, "deactivated": {"title": "Deactivated", "type": "boolean", "readOnly": true}, "assignments": {"title": "Assignments", "type": "string", "readOnly": true}, "remaining": {"title": "Remaining", "type": "string", "readOnly": true}, "available": {"title": "Available", "type": "string", "readOnly": true}}}, "Tutor": {"required": ["username"], "type": "object", "properties": {"pk": {"title": "User id", "type": "string", "format": "uuid", "readOnly": true}, "password": {"title": "Password", "type": "string", "minLength": 1}, "isActive": {"title": "Active", "description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", "type": "boolean"}, "username": {"title": "Username", "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", "type": "string", "pattern": "^[\\w.@+-]+$", "maxLength": 150, "minLength": 1}, "feedbackCreated": {"title": "Feedback created", "type": "string", "readOnly": true}, "feedbackValidated": {"title": "Feedback validated", "type": "string", "readOnly": true}}}, "UserAccount": {"required": ["password"], "type": "object", "properties": {"pk": {"title": "User id", "type": "string", "format": "uuid", "readOnly": true}, "username": {"title": "Username", "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", "type": "string", "readOnly": true, "minLength": 1}, "role": {"title": "Role", "type": "string", "enum": ["Student", "Tutor", "Reviewer"], "readOnly": true}, "isAdmin": {"title": "Is admin", "type": "boolean", "readOnly": true}, "password": {"title": "Password", "type": "string", "maxLength": 128, "minLength": 1}}}}}
\ No newline at end of file
-- 
GitLab


From 2b988ae3af379045191a2e634f7c66ae439dab97 Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Fri, 10 Aug 2018 14:49:28 +0200
Subject: [PATCH 3/9] Refactored code to use new camelCase Api

---
 frontend/src/api.ts                           |  32 +--
 .../src/components/CorrectionStatistics.vue   |  14 +-
 frontend/src/components/DataExport.vue        |   2 +-
 .../src/components/PasswordChangeDialog.vue   |   4 +-
 frontend/src/components/SubmissionType.vue    |  10 +-
 .../feedback_list/FeedbackTable.vue           |  16 +-
 frontend/src/components/mixins/mixins.js      |   2 +-
 .../components/student/ExamInformation.vue    |   6 +-
 .../src/components/student/SubmissionList.vue |   6 +-
 .../components/student_list/StudentList.vue   |  18 +-
 .../student_list/StudentListMenu.vue          |   2 +-
 .../submission_notes/SubmissionCorrection.vue |  14 +-
 .../submission_notes/base/FeedbackComment.vue |  12 +-
 .../AnnotatedSubmissionBottomToolbar.vue      |   4 +-
 .../subscriptions/SubscriptionForList.vue     |   8 +-
 .../src/components/tutor_list/TutorList.vue   |  16 +-
 frontend/src/models.ts                        | 213 +++++++++---------
 frontend/src/pages/student/StudentLayout.vue  |   4 +-
 .../pages/student/StudentSubmissionPage.vue   |   6 +-
 frontend/src/store/getters.ts                 |   4 +-
 frontend/src/store/modules/authentication.ts  |   4 +-
 .../modules/feedback_list/feedback-table.ts   |   8 +-
 .../src/store/modules/submission-notes.ts     |  24 +-
 frontend/src/store/modules/subscriptions.ts   |  30 +--
 frontend/src/store/mutations.ts               |   8 +-
 frontend/src/store/store.ts                   |   3 +-
 26 files changed, 237 insertions(+), 233 deletions(-)

diff --git a/frontend/src/api.ts b/frontend/src/api.ts
index 57bf23bf..a50422e5 100644
--- a/frontend/src/api.ts
+++ b/frontend/src/api.ts
@@ -1,17 +1,17 @@
 import axios, {AxiosInstance, AxiosPromise, AxiosResponse} from 'axios'
 import {Credentials} from '@/store/modules/authentication'
 import {
-    Assignment,
-    Exam,
-    Feedback, FeedbackComment,
-    JSONWebToken, Statistics,
-    StudentInfo,
-    StudentInfoForListView,
-    Submission,
-    SubmissionNoType, SubmissionType,
-    Subscription,
-    Tutor, UserAccount
-} from "@/models";
+  Assignment,
+  Exam,
+  Feedback, FeedbackComment,
+  JSONWebToken, Statistics,
+  StudentInfo,
+  StudentInfoForListView,
+  Submission,
+  SubmissionNoType, SubmissionType,
+  Subscription,
+  Tutor, UserAccount
+} from '@/models'
 
 function addFieldsToUrl ({url, fields = []}: {url: string, fields?: string[]}): string {
   return fields.length > 0 ? url + '?fields=pk,' + fields : url
@@ -135,8 +135,8 @@ export async function fetchStatistics (opt = {fields: []}): Promise<Statistics>
 }
 
 export async function subscribeTo (type: Subscription.QueryTypeEnum,
-                                   key: string,
-                                   stage: Subscription.FeedbackStageEnum): Promise<Subscription> {
+  key: string,
+  stage: Subscription.FeedbackStageEnum): Promise<Subscription> {
   let data: Subscription = {
     queryType: type
   }
@@ -152,8 +152,8 @@ export async function subscribeTo (type: Subscription.QueryTypeEnum,
 }
 
 export async function createAssignment (
-    {subscription = undefined, subscriptionPk = ''}:
-        {subscription?: Subscription, subscriptionPk?: string}): Promise<Assignment> {
+  {subscription = undefined, subscriptionPk = ''}:
+  {subscription?: Subscription, subscriptionPk?: string}): Promise<Assignment> {
   const data = {
     subscription: subscription ? subscription.pk : subscriptionPk
   }
@@ -188,7 +188,7 @@ export async function deleteAssignment ({assignment}: {assignment: Assignment}):
 
 export async function deleteComment (comment = {pk: undefined}): Promise<AxiosResponse<void>> {
   const url = `/api/feedback-comment/${comment.pk}/`
-  return await ax.delete(url)
+  return ax.delete(url)
 }
 
 export async function patchComment (comment = {pk: undefined}): Promise<FeedbackComment> {
diff --git a/frontend/src/components/CorrectionStatistics.vue b/frontend/src/components/CorrectionStatistics.vue
index c6d877b2..a3bf9ead 100644
--- a/frontend/src/components/CorrectionStatistics.vue
+++ b/frontend/src/components/CorrectionStatistics.vue
@@ -5,25 +5,25 @@
       </v-card-title>
       <div v-if="loaded">
         <ul class="inline-list mx-3">
-          <li>Submissions per student: <span>{{statistics.submissions_per_student}}</span></li>
-          <li>Submissions per type: <span>{{statistics.submissions_per_type}}</span></li>
+          <li>Submissions per student: <span>{{statistics.submissionsPerStudent}}</span></li>
+          <li>Submissions per type: <span>{{statistics.submissionsPerType}}</span></li>
           <li>Curr. mean score:
             <span>
-              {{statistics.current_mean_score === null ? 'N.A.' : statistics.current_mean_score.toFixed(2)}}
+              {{statistics.currentMeanScore === null ? 'N.A.' : statistics.currentMeanScore.toFixed(2)}}
             </span>
           </li>
         </ul>
         <v-divider class="mx-2 my-2"></v-divider>
-        <div v-for="(progress, index) in statistics.submission_type_progress" :key="index">
+        <div v-for="(progress, index) in statistics.submissionTypeProgress" :key="index">
           <v-card-title class="py-0">
             {{progress.name}}
           </v-card-title>
           <div class="mx-3">
             <v-progress-linear
-              :value="progress.feedback_final / progress.submission_count * 100"
+              :value="progress.feedbackFinal / progress.submissionCount * 100"
               buffer
-              :buffer-value="(progress.feedback_in_validation + progress.feedback_final) * 100 / progress.submission_count"
-              :color="progress.feedback_final === progress.submission_count ? 'green' : 'blue'"
+              :buffer-value="(progress.feedbackInValidation + progress.feedbackFinal) * 100 / progress.submissionCount"
+              :color="progress.feedbackFinal === progress.submissionCount ? 'green' : 'blue'"
             />
           </div>
         </div>
diff --git a/frontend/src/components/DataExport.vue b/frontend/src/components/DataExport.vue
index 451fd950..269d2a27 100644
--- a/frontend/src/components/DataExport.vue
+++ b/frontend/src/components/DataExport.vue
@@ -125,7 +125,7 @@ export default {
       return students.map(studentData => {
         return {
           ...studentData,
-          Matrikel: this.$store.state.studentMap[studentData.Matrikel].matrikel_no,
+          Matrikel: this.$store.state.studentMap[studentData.Matrikel].matrikelNo,
           Name: this.$store.state.studentMap[studentData.Matrikel].name
         }
       })
diff --git a/frontend/src/components/PasswordChangeDialog.vue b/frontend/src/components/PasswordChangeDialog.vue
index 60d1b072..03e4758a 100644
--- a/frontend/src/components/PasswordChangeDialog.vue
+++ b/frontend/src/components/PasswordChangeDialog.vue
@@ -67,8 +67,8 @@ export default {
   methods: {
     submitChange () {
       const data = {
-        old_password: this.currentPassword,
-        new_password: this.newPassword
+        oldPassword: this.currentPassword,
+        newPassword: this.newPassword
       }
       changePassword(this.userPk, data).then(() => {
         this.$notify({
diff --git a/frontend/src/components/SubmissionType.vue b/frontend/src/components/SubmissionType.vue
index e952ee27..253ebad3 100644
--- a/frontend/src/components/SubmissionType.vue
+++ b/frontend/src/components/SubmissionType.vue
@@ -1,7 +1,7 @@
 <template>
   <v-layout column>
     <v-card>
-      <v-card-title class="title mb-2">{{ name }} - Full score: {{ full_score }}</v-card-title>
+      <v-card-title class="title mb-2">{{ name }} - Full score: {{ fullScore }}</v-card-title>
       <v-expansion-panel expand>
         <v-expansion-panel-content
           v-for="(item, i) in typeItems"
@@ -22,7 +22,7 @@
           <v-flex v-else-if="item.title === 'Solution'">
           <pre
             class="elevation-2 solution-code pl-2"
-            :class="programming_language"
+            :class="programmingLanguage"
           ><span v-html="highlightedSolution"></span></pre>
           </v-flex>
         </v-expansion-panel-content>
@@ -49,11 +49,11 @@ export default {
       type: String,
       required: true
     },
-    full_score: {
+    fullScore: {
       type: Number,
       required: true
     },
-    programming_language: {
+    programmingLanguage: {
       type: String,
       default: 'c'
     },
@@ -93,7 +93,7 @@ export default {
       }
     },
     highlightedSolution () {
-      return highlight(this.programming_language, this.solution, true).value
+      return highlight(this.programmingLanguage, this.solution, true).value
     },
     backgroundColor () {
       return this.darkMode ? 'grey' : '#F3F3F3'
diff --git a/frontend/src/components/feedback_list/FeedbackTable.vue b/frontend/src/components/feedback_list/FeedbackTable.vue
index c75bf049..c00dc781 100644
--- a/frontend/src/components/feedback_list/FeedbackTable.vue
+++ b/frontend/src/components/feedback_list/FeedbackTable.vue
@@ -21,14 +21,14 @@
       hide-actions
     >
       <template slot="items" slot-scope="props">
-        <tr @click="showSubmission(props.item.of_submission)"
+        <tr @click="showSubmission(props.item.ofSubmission)"
             class="feedback-row"
         >
-          <td>{{props.item.of_submission_type}}</td>
+          <td>{{props.item.ofSubmissionType}}</td>
           <td>{{props.item.score}}</td>
           <td>{{new Date(props.item.created).toLocaleString()}}</td>
           <td>
-            <v-icon v-if="props.item.is_final">check</v-icon>
+            <v-icon v-if="props.item.isFinal">check</v-icon>
             <v-icon v-else>clear</v-icon>
           </td>
         </tr>
@@ -72,7 +72,7 @@ export default {
         {
           text: 'Type',
           align: 'left',
-          value: 'of_submission_type'
+          value: 'ofSubmissionType'
         },
         {
           text: 'score',
@@ -110,8 +110,8 @@ export default {
     prefetchFilteredItems (items) {
       if (items.length < this.prefetchWhenLessItems) {
         for (let item of items) {
-          if (!this.$store.state.submissions[item.of_submission]) {
-            this.prefetchSubmission(item.of_submission)
+          if (!this.$store.state.submissions[item.ofSubmission]) {
+            this.prefetchSubmission(item.ofSubmission)
           }
         }
       }
@@ -130,10 +130,10 @@ export default {
       }
     },
     checkFinal (feedback) {
-      return this.showFinal ? true : !feedback.is_final
+      return this.showFinal ? true : !feedback.isFinal
     },
     commentFilter (feedback, search, filter) {
-      return Object.values(feedback.feedback_lines).some(line => line.some(comment => {
+      return Object.values(feedback.feedbackLines).some(line => line.some(comment => {
         return filter(comment.text, search)
       }))
     },
diff --git a/frontend/src/components/mixins/mixins.js b/frontend/src/components/mixins/mixins.js
index b1365f3a..4bd52cef 100644
--- a/frontend/src/components/mixins/mixins.js
+++ b/frontend/src/components/mixins/mixins.js
@@ -6,7 +6,7 @@ export var parseCSVMapMixin = {
       return lines.reduce((acc, curr) => {
         if (curr) {
           let [key, matrikelNo, name] = curr.split(';')
-          acc[key] = {matrikel_no: matrikelNo, name}
+          acc[key] = {matrikelNo: matrikelNo, name}
         }
         return acc
       }, {})
diff --git a/frontend/src/components/student/ExamInformation.vue b/frontend/src/components/student/ExamInformation.vue
index 0cdf58d9..6c079e8f 100644
--- a/frontend/src/components/student/ExamInformation.vue
+++ b/frontend/src/components/student/ExamInformation.vue
@@ -3,18 +3,18 @@
     <tbody>
       <tr>
         <th>Module</th>
-        <td>{{ exam.module_reference }}</td>
+        <td>{{ exam.moduleReference }}</td>
       </tr>
       <tr>
         <th>Pass score</th>
-        <td>{{ exam.pass_score }}</td>
+        <td>{{ exam.passScore }}</td>
       </tr>
       <tr v-if="exam.passOnly">
         <th>Pass only exam!</th>
       </tr>
       <tr>
         <th>Total score</th>
-        <td>{{ exam.total_score }}</td>
+        <td>{{ exam.totalScore }}</td>
       </tr>
     </tbody>
   </table>
diff --git a/frontend/src/components/student/SubmissionList.vue b/frontend/src/components/student/SubmissionList.vue
index 835853d8..b7dd7052 100644
--- a/frontend/src/components/student/SubmissionList.vue
+++ b/frontend/src/components/student/SubmissionList.vue
@@ -9,7 +9,7 @@
       <template slot="items" slot-scope="props">
         <td>{{ props.item.type.name }}</td>
         <td class="text-xs-right">{{ props.item.feedback ?  props.item.feedback.score : 'N/A'}}</td>
-        <td class="text-xs-right">{{ props.item.type.full_score }}</td>
+        <td class="text-xs-right">{{ props.item.type.fullScore }}</td>
         <td class="text-xs-right">
           <v-btn :to="`/submission/${props.item.type.pk}`" color="orange lighten-2">
             <v-icon>chevron_right</v-icon>
@@ -43,7 +43,7 @@ export default {
         {
           text: 'Maximum Score',
           align: 'right',
-          value: 'type.full_score'
+          value: 'type.fullScore'
         },
         {
           text: 'View',
@@ -64,7 +64,7 @@ export default {
       return this.submissions.map(a => a.feedback && a.feedback.score).reduce((a, b) => a + b)
     },
     sumFullScore () {
-      return this.submissions.map(a => a.type.full_score).reduce((a, b) => a + b)
+      return this.submissions.map(a => a.type.fullScore).reduce((a, b) => a + b)
     },
     pointRatio () {
       return ((this.sumScore / this.sumFullScore) * 100).toFixed(2)
diff --git a/frontend/src/components/student_list/StudentList.vue b/frontend/src/components/student_list/StudentList.vue
index 59b22e35..1e2c0e72 100644
--- a/frontend/src/components/student_list/StudentList.vue
+++ b/frontend/src/components/student_list/StudentList.vue
@@ -50,10 +50,10 @@
             {{props.item.name}}
             <v-tooltip top>
               <template slot="activator">
-                <v-icon small v-if="!props.item.is_active">lock</v-icon>
+                <v-icon small v-if="!props.item.isActive">lock</v-icon>
                 <v-icon small v-else>lock_open</v-icon>
               </template>
-              <span v-if="!props.item.is_active">Student doesn't have access.</span>
+              <span v-if="!props.item.isActive">Student doesn't have access.</span>
               <span v-else>Student has access.</span>
             </v-tooltip>
           </td>
@@ -87,14 +87,14 @@
         <v-card flat>
           <v-card-text>
             <v-btn @click="changeActiveStatus(props.item)">
-              {{props.item.is_active ? 'Revoke access' : 'Grant access'}}
+              {{props.item.isActive ? 'Revoke access' : 'Grant access'}}
             </v-btn>
             <ul class="student-info-list">
               <li>
                 <b>Modul:</b> {{props.item.exam}}
               </li>
               <li>
-                <b>MatrikelNr:</b> {{props.item.matrikel_no}}
+                <b>MatrikelNr:</b> {{props.item.matrikelNo}}
               </li>
             </ul>
           </v-card-text>
@@ -163,11 +163,11 @@ export default {
           return {
             pk: student.pk,
             user: student.user,
-            user_pk: student.user_pk,
+            userPk: student.userPk,
             exam: student.exam,
             name: student.name,
-            is_active: student.is_active,
-            matrikel_no: student.matrikel_no,
+            isActive: student.isActive,
+            matrikelNo: student.matrikelNo,
             ...this.reduceArrToDict(student.submissions, 'type'),
             total: this.sumSubmissionScores(student.submissions)
           }
@@ -187,8 +187,8 @@ export default {
       }, {})
     },
     changeActiveStatus (student) {
-      changeActiveForUser(student.user_pk, !student.is_active).then(() => {
-        this.getStudents({studentPks: [student.pk], fields: ['is_active']})
+      changeActiveForUser(student.userPk, !student.isActive).then(() => {
+        this.getStudents({studentPks: [student.pk], fields: ['isActive']})
       }).catch(() => {
         this.$notify({
           title: 'Error',
diff --git a/frontend/src/components/student_list/StudentListMenu.vue b/frontend/src/components/student_list/StudentListMenu.vue
index b60445fb..ec0129b0 100644
--- a/frontend/src/components/student_list/StudentListMenu.vue
+++ b/frontend/src/components/student_list/StudentListMenu.vue
@@ -20,7 +20,7 @@ export default {
   computed: {
     studentsActive () {
       const firstStudent = Object.values(this.$store.state.students)[0]
-      return firstStudent ? firstStudent.is_active === true : false
+      return firstStudent ? firstStudent.isActive === true : false
     },
     items () {
       return [
diff --git a/frontend/src/components/submission_notes/SubmissionCorrection.vue b/frontend/src/components/submission_notes/SubmissionCorrection.vue
index db73d18d..3c70cffe 100644
--- a/frontend/src/components/submission_notes/SubmissionCorrection.vue
+++ b/frontend/src/components/submission_notes/SubmissionCorrection.vue
@@ -17,10 +17,10 @@
                 <feedback-comment
                   v-for="(comment, index) in origFeedback[lineNo]"
                   v-bind="comment"
-                  :visible_to_student="updatedFeedback[lineNo] ? false : comment.visible_to_student"
+                  :visibleToStudent="updatedFeedback[lineNo] ? false : comment.visibleToStudent"
                   :line-no="lineNo"
                   :key="index"
-                  :deletable="comment.of_tutor === user || isReviewer"
+                  :deletable="comment.ofTutor === user || isReviewer"
                   @click.native="toggleEditorOnLine(lineNo, comment)"
                 />
               </div>
@@ -46,7 +46,7 @@
         class="mt-1 elevation-1"
         slot="footer"
         :loading="loading"
-        :fullScore="submissionObj['full_score']"
+        :fullScore="submissionObj['fullScore']"
         :skippable="assignment !== undefined"
         :feedback="feedbackObj ? feedbackObj : {}"
         @submitFeedback="submitFeedback"
@@ -99,8 +99,8 @@ export default {
       user: state => state.authentication.user.username,
       showEditorOnLine: state => state.submissionNotes.ui.showEditorOnLine,
       selectedComment: state => state.submissionNotes.ui.selectedCommentOnLine,
-      origFeedback: state => state.submissionNotes.origFeedback.feedback_lines,
-      updatedFeedback: state => state.submissionNotes.updatedFeedback.feedback_lines,
+      origFeedback: state => state.submissionNotes.origFeedback.feedbackLines,
+      updatedFeedback: state => state.submissionNotes.updatedFeedback.feedbackLines,
       showFeedback: state => state.submissionNotes.ui.showFeedback
     }),
     ...mapGetters([
@@ -146,8 +146,8 @@ export default {
     },
     shortPollOrigFeedback () {
       this.feedbackShortPollInterval = setInterval(() => {
-        if (this.feedbackObj && this.feedbackObj.of_submission) {
-          this.$store.dispatch('getFeedback', {ofSubmission: this.feedbackObj.of_submission}).then(feedback => {
+        if (this.feedbackObj && this.feedbackObj.ofSubmission) {
+          this.$store.dispatch('getFeedback', {ofSubmission: this.feedbackObj.ofSubmission}).then(feedback => {
             this.$store.commit(subNotesNamespace(subNotesMut.SET_ORIG_FEEDBACK), feedback)
           })
         }
diff --git a/frontend/src/components/submission_notes/base/FeedbackComment.vue b/frontend/src/components/submission_notes/base/FeedbackComment.vue
index 5b29b56c..989689e6 100644
--- a/frontend/src/components/submission_notes/base/FeedbackComment.vue
+++ b/frontend/src/components/submission_notes/base/FeedbackComment.vue
@@ -2,10 +2,10 @@
   <div class="dialog-box">
     <div class="body elevation-1" :style="{borderColor: borderColor, backgroundColor}">
       <span class="tip tip-up" :style="{borderBottomColor: borderColor}"></span>
-      <span v-if="of_tutor" class="of-tutor">Of tutor: {{of_tutor}}</span>
+      <span v-if="ofTutor" class="of-tutor">Of tutor: {{ofTutor}}</span>
       <span class="comment-created">{{parsedCreated}}</span>
-      <div class="visibility-icon" v-if="show_visibility_icon">
-        <v-tooltip top v-if="visible_to_student" size="20px">
+      <div class="visibility-icon" v-if="showVisibilityIcon">
+        <v-tooltip top v-if="visibleToStudent" size="20px">
           <v-icon
             slot="activator"
             size="20px"
@@ -54,7 +54,7 @@ export default {
       type: String,
       required: false
     },
-    of_tutor: {
+    ofTutor: {
       type: String,
       required: false
     },
@@ -66,11 +66,11 @@ export default {
       type: Boolean,
       default: false
     },
-    visible_to_student: {
+    visibleToStudent: {
       type: Boolean,
       default: true
     },
-    show_visibility_icon: {
+    showVisibilityIcon: {
       type: Boolean,
       default: true
     }
diff --git a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue
index 4c03f1a0..c26aea45 100644
--- a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue
+++ b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue
@@ -108,8 +108,8 @@ export default {
       if (this.$route.name === 'subscription') {
         return !this.$store.getters['submissionNotes/isFeedbackCreation'] || this.$store.getters.isReviewer
       } else {
-        if (this.feedback.hasOwnProperty('is_final')) {
-          return this.feedback.is_final
+        if (this.feedback.hasOwnProperty('isFinal')) {
+          return this.feedback.isFinal
         } else {
           return !this.$store.getters['submissionNotes/isFeedbackCreation'] || this.$store.getters.isReviewer
         }
diff --git a/frontend/src/components/subscriptions/SubscriptionForList.vue b/frontend/src/components/subscriptions/SubscriptionForList.vue
index 867eaa57..b8284c04 100644
--- a/frontend/src/components/subscriptions/SubscriptionForList.vue
+++ b/frontend/src/components/subscriptions/SubscriptionForList.vue
@@ -25,11 +25,11 @@ export default {
       types: String,
       required: true
     },
-    feedback_stage: {
+    feedbackStage: {
       type: String,
       required: true
     },
-    query_type: {
+    queryType: {
       type: String,
       required: true
     },
@@ -41,14 +41,14 @@ export default {
       type: Array,
       required: true
     },
-    query_key: {
+    queryKey: {
       type: String
     }
   },
   computed: {
     name () {
       return this.$store.getters.resolveSubscriptionKeyToName(
-        {query_key: this.query_key, query_type: this.query_type})
+        {queryKey: this.queryKey, queryType: this.queryType})
     },
     active () {
       return !!this.available || this.assignments.length > 0
diff --git a/frontend/src/components/tutor_list/TutorList.vue b/frontend/src/components/tutor_list/TutorList.vue
index 9e0d2f67..2b9abb0a 100644
--- a/frontend/src/components/tutor_list/TutorList.vue
+++ b/frontend/src/components/tutor_list/TutorList.vue
@@ -9,16 +9,16 @@
     >
       <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>
+        <td class="text-xs-right">{{props.item.feedbackCreated}}</td>
+        <td class="text-xs-right">{{props.item.feedbackValidated}}</td>
         <td class="text-xs-right">
           <v-btn icon @click="changeActiveStatus(props.item)">
             <v-tooltip top>
               <template slot="activator">
-                <v-icon small v-if="!props.item.is_active">lock</v-icon>
+                <v-icon small v-if="!props.item.isActive">lock</v-icon>
                 <v-icon small v-else>lock_open</v-icon>
               </template>
-              <span v-if="!props.item.is_active">Grant access</span>
+              <span v-if="!props.item.isActive">Grant access</span>
               <span v-else>Revoke access</span>
             </v-tooltip>
           </v-btn>
@@ -46,17 +46,17 @@ export default {
         {
           text: '# created',
           align: 'right',
-          value: 'feedback_created'
+          value: 'feedbackCreated'
         },
         {
           text: '# validated',
           align: 'right',
-          value: 'feedback_validated'
+          value: 'feedbackValidated'
         },
         {
           text: 'Has Access',
           align: 'right',
-          value: 'is_active'
+          value: 'isActive'
         }
       ]
     }
@@ -71,7 +71,7 @@ export default {
       'getTutors'
     ]),
     changeActiveStatus (tutor) {
-      changeActiveForUser(tutor.pk, !tutor.is_active).then(() => {
+      changeActiveForUser(tutor.pk, !tutor.isActive).then(() => {
         this.getTutors()
       }).catch(() => {
         this.$notify({
diff --git a/frontend/src/models.ts b/frontend/src/models.ts
index 485884b2..87153cb7 100644
--- a/frontend/src/models.ts
+++ b/frontend/src/models.ts
@@ -9,31 +9,31 @@ export interface Assignment {
      * @type {string}
      * @memberof Assignment
      */
-    pk?: string;
+    pk?: string
     /**
      *
      * @type {string}
      * @memberof Assignment
      */
-    submission?: string;
+    submission?: string
     /**
      *
      * @type {boolean}
      * @memberof Assignment
      */
-    isDone?: boolean;
+    isDone?: boolean
     /**
      *
      * @type {string}
      * @memberof Assignment
      */
-    owner?: string;
+    owner?: string
     /**
      *
      * @type {string}
      * @memberof Assignment
      */
-    stage?: string;
+    stage?: string
 }
 
 /**
@@ -47,31 +47,31 @@ export interface Exam {
      * @type {string}
      * @memberof Exam
      */
-    pk?: string;
+    pk?: string
     /**
      *
      * @type {string}
      * @memberof Exam
      */
-    moduleReference: string;
+    moduleReference: string
     /**
      *
      * @type {number}
      * @memberof Exam
      */
-    totalScore: number;
+    totalScore: number
     /**
      *
      * @type {number}
      * @memberof Exam
      */
-    passScore: number;
+    passScore: number
     /**
      *
      * @type {boolean}
      * @memberof Exam
      */
-    passOnly?: boolean;
+    passOnly?: boolean
 }
 
 /**
@@ -85,49 +85,49 @@ export interface Feedback {
      * @type {number}
      * @memberof Feedback
      */
-    pk?: number;
+    pk?: number
     /**
      *
      * @type {string}
      * @memberof Feedback
      */
-    ofSubmission: string;
+    ofSubmission: string
     /**
      *
      * @type {boolean}
      * @memberof Feedback
      */
-    isFinal?: boolean;
+    isFinal?: boolean
     /**
      *
      * @type {number}
      * @memberof Feedback
      */
-    score?: number;
+    score?: number
     /**
      *
      * @type {Array<FeedbackComment>}
      * @memberof Feedback
      */
-    feedbackLines?: {[lineNo: number]: FeedbackComment[]};
+    feedbackLines?: {[lineNo: number]: FeedbackComment[]}
     /**
      *
      * @type {Date}
      * @memberof Feedback
      */
-    created?: string;
+    created?: string
     /**
      *
      * @type {string}
      * @memberof Feedback
      */
-    ofSubmissionType?: string;
+    ofSubmissionType?: string
     /**
      *
      * @type {string}
      * @memberof Feedback
      */
-    feedbackStageForUser?: string;
+    feedbackStageForUser?: string
 }
 
 /**
@@ -141,37 +141,37 @@ export interface FeedbackComment {
      * @type {string}
      * @memberof FeedbackComment
      */
-    pk?: string;
+    pk?: string
     /**
      *
      * @type {string}
      * @memberof FeedbackComment
      */
-    text: string;
+    text: string
     /**
      *
      * @type {Date}
      * @memberof FeedbackComment
      */
-    created?: string;
+    created?: string
     /**
      *
      * @type {string}
      * @memberof FeedbackComment
      */
-    ofTutor?: string;
+    ofTutor?: string
     /**
      *
      * @type {number}
      * @memberof FeedbackComment
      */
-    ofLine?: number;
+    ofLine?: number
     /**
      *
      * @type {boolean}
      * @memberof FeedbackComment
      */
-    visibleToStudent?: boolean;
+    visibleToStudent?: boolean
 }
 
 /**
@@ -185,13 +185,13 @@ export interface Credentials {
      * @type {string}
      * @memberof JSONWebToken
      */
-    username: string;
+    username: string
     /**
      *
      * @type {string}
      * @memberof JSONWebToken
      */
-    password: string;
+    password: string
 }
 
 
@@ -217,43 +217,43 @@ export interface StudentInfo {
      * @type {string}
      * @memberof StudentInfo
      */
-    pk?: string;
+    pk?: string
     /**
      *
      * @type {string}
      * @memberof StudentInfo
      */
-    name?: string;
+    name?: string
     /**
      *
      * @type {string}
      * @memberof StudentInfo
      */
-    user: string;
+    user: string
     /**
      *
      * @type {string}
      * @memberof StudentInfo
      */
-    matrikelNo?: string;
+    matrikelNo?: string
     /**
      *
      * @type {Exam}
      * @memberof StudentInfo
      */
-    exam: Exam;
+    exam: Exam
     /**
      *
      * @type {Array<SubmissionList>}
      * @memberof StudentInfo
      */
-    submissions: Array<SubmissionList>;
+    submissions: Array<SubmissionList>
     /**
      *
      * @type {boolean}
      * @memberof StudentInfo
      */
-    passesExam?: boolean;
+    passesExam?: boolean
 }
 
 /**
@@ -267,55 +267,55 @@ export interface StudentInfoForListView {
      * @type {string}
      * @memberof StudentInfoForListView
      */
-    pk?: string;
+    pk?: string
     /**
      *
      * @type {string}
      * @memberof StudentInfoForListView
      */
-    name?: string;
+    name?: string
     /**
      *
      * @type {string}
      * @memberof StudentInfoForListView
      */
-    user?: string;
+    user?: string
     /**
      *
      * @type {string}
      * @memberof StudentInfoForListView
      */
-    userPk?: string;
+    userPk?: string
     /**
      *
      * @type {string}
      * @memberof StudentInfoForListView
      */
-    exam?: string;
+    exam?: string
     /**
      *
      * @type {Array<SubmissionNoTextFields>}
      * @memberof StudentInfoForListView
      */
-    submissions: Array<SubmissionNoTextFields>;
+    submissions: Array<SubmissionNoTextFields>
     /**
      *
      * @type {string}
      * @memberof StudentInfoForListView
      */
-    matrikelNo?: string;
+    matrikelNo?: string
     /**
      *
      * @type {boolean}
      * @memberof StudentInfoForListView
      */
-    passesExam?: boolean;
+    passesExam?: boolean
     /**
      *
      * @type {boolean}
      * @memberof StudentInfoForListView
      */
-    isActive: boolean;
+    isActive: boolean
 }
 
 /**
@@ -329,31 +329,31 @@ export interface Submission {
      * @type {string}
      * @memberof Submission
      */
-    pk?: string;
+    pk?: string
     /**
      *
      * @type {SubmissionType}
      * @memberof Submission
      */
-    type: SubmissionType;
+    type: SubmissionType
     /**
      *
      * @type {string}
      * @memberof Submission
      */
-    text?: string;
+    text?: string
     /**
      *
      * @type {VisibleCommentFeedback}
      * @memberof Submission
      */
-    feedback: VisibleCommentFeedback;
+    feedback: VisibleCommentFeedback
     /**
      *
      * @type {Array<Test>}
      * @memberof Submission
      */
-    tests: Array<Test>;
+    tests: Array<Test>
 }
 
 /**
@@ -367,19 +367,19 @@ export interface SubmissionList {
      * @type {string}
      * @memberof SubmissionList
      */
-    pk?: string;
+    pk?: string
     /**
      *
      * @type {SubmissionTypeList}
      * @memberof SubmissionList
      */
-    type: SubmissionTypeList;
+    type: SubmissionTypeList
     /**
      *
      * @type {Feedback}
      * @memberof SubmissionList
      */
-    feedback: Feedback;
+    feedback: Feedback
 }
 
 /**
@@ -393,31 +393,31 @@ export interface SubmissionNoTextFields {
      * @type {string}
      * @memberof SubmissionNoTextFields
      */
-    pk?: string;
+    pk?: string
     /**
      *
      * @type {string}
      * @memberof SubmissionNoTextFields
      */
-    type: string;
+    type: string
     /**
      *
      * @type {string}
      * @memberof SubmissionNoTextFields
      */
-    score?: string;
+    score?: string
     /**
      *
      * @type {string}
      * @memberof SubmissionNoTextFields
      */
-    _final?: string;
+    final?: string
     /**
      *
      * @type {string}
      * @memberof SubmissionNoTextFields
      */
-    fullScore?: string;
+    fullScore?: string
 }
 
 /**
@@ -431,37 +431,37 @@ export interface SubmissionNoType {
      * @type {string}
      * @memberof SubmissionNoType
      */
-    pk?: string;
+    pk?: string
     /**
      *
      * @type {string}
      * @memberof SubmissionNoType
      */
-    type: string;
+    type: string
     /**
      *
      * @type {string}
      * @memberof SubmissionNoType
      */
-    fullScore?: string;
+    fullScore?: string
     /**
      *
      * @type {string}
      * @memberof SubmissionNoType
      */
-    text?: string;
+    text?: string
     /**
      *
      * @type {Feedback}
      * @memberof SubmissionNoType
      */
-    feedback: Feedback;
+    feedback: Feedback
     /**
      *
      * @type {Array<Test>}
      * @memberof SubmissionNoType
      */
-    tests: Array<Test>;
+    tests: Array<Test>
 }
 
 /**
@@ -475,37 +475,37 @@ export interface SubmissionType {
      * @type {string}
      * @memberof SubmissionType
      */
-    pk?: string;
+    pk?: string
     /**
      *
      * @type {string}
      * @memberof SubmissionType
      */
-    name: string;
+    name: string
     /**
      *
      * @type {number}
      * @memberof SubmissionType
      */
-    fullScore?: number;
+    fullScore?: number
     /**
      *
      * @type {string}
      * @memberof SubmissionType
      */
-    description: string;
+    description: string
     /**
      *
      * @type {string}
      * @memberof SubmissionType
      */
-    solution: string;
+    solution: string
     /**
      *
      * @type {string}
      * @memberof SubmissionType
      */
-    programmingLanguage?: SubmissionType.ProgrammingLanguageEnum;
+    programmingLanguage?: SubmissionType.ProgrammingLanguageEnum
 }
 
 /**
@@ -534,27 +534,28 @@ export interface SubmissionTypeList {
      * @type {string}
      * @memberof SubmissionTypeList
      */
-    pk?: string;
+    pk?: string
+
     /**
      *
      * @type {string}
      * @memberof SubmissionTypeList
      */
-    name: string;
+    name: string
     /**
      *
      * @type {number}
      * @memberof SubmissionTypeList
      */
-    fullScore?: number;
+    fullScore?: number
 }
 
 export interface SubmissionTypeProgress {
-    pk?: string,
-    name: string,
-    feedbackFinal: number,
-    feedbackInValidation: number,
-    feedbackInConflict: number,
+    pk?: string
+    name: string
+    feedbackFinal: number
+    feedbackInValidation: number
+    feedbackInConflict: number
     submissionCount: number
 }
 
@@ -569,55 +570,55 @@ export interface Subscription {
      * @type {string}
      * @memberof Subscription
      */
-    pk?: string;
+    pk?: string
     /**
      *
      * @type {string}
      * @memberof Subscription
      */
-    owner?: string;
+    owner?: string
     /**
      *
      * @type {string}
      * @memberof Subscription
      */
-    queryType?: Subscription.QueryTypeEnum;
+    queryType?: Subscription.QueryTypeEnum
     /**
      *
      * @type {string}
      * @memberof Subscription
      */
-    queryKey?: string;
+    queryKey?: string
     /**
      *
      * @type {string}
      * @memberof Subscription
      */
-    feedbackStage?: Subscription.FeedbackStageEnum;
+    feedbackStage?: Subscription.FeedbackStageEnum
     /**
      *
      * @type {boolean}
      * @memberof Subscription
      */
-    deactivated?: boolean;
+    deactivated?: boolean
     /**
      *
      * @type {string}
      * @memberof Subscription
      */
-    assignments?: string;
+    assignments?: string
     /**
      *
      * @type {string}
      * @memberof Subscription
      */
-    remaining?: string;
+    remaining?: string
     /**
      *
      * @type {string}
      * @memberof Subscription
      */
-    available?: string;
+    available?: string
 }
 
 /**
@@ -657,25 +658,25 @@ export interface Test {
      * @type {string}
      * @memberof Test
      */
-    pk?: string;
+    pk?: string
     /**
      *
      * @type {string}
      * @memberof Test
      */
-    name: string;
+    name: string
     /**
      *
      * @type {string}
      * @memberof Test
      */
-    label: string;
+    label: string
     /**
      *
      * @type {string}
      * @memberof Test
      */
-    annotation: string;
+    annotation: string
 }
 
 /**
@@ -689,37 +690,37 @@ export interface Tutor {
      * @type {string}
      * @memberof Tutor
      */
-    pk?: string;
+    pk?: string
     /**
      *
      * @type {string}
      * @memberof Tutor
      */
-    password?: string;
+    password?: string
     /**
      * Designates whether this user should be treated as active. Unselect this instead of deleting accounts.
      * @type {boolean}
      * @memberof Tutor
      */
-    isActive?: boolean;
+    isActive?: boolean
     /**
      * Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.
      * @type {string}
      * @memberof Tutor
      */
-    username: string;
+    username: string
     /**
      *
      * @type {string}
      * @memberof Tutor
      */
-    feedbackCreated?: string;
+    feedbackCreated?: string
     /**
      *
      * @type {string}
      * @memberof Tutor
      */
-    feedbackValidated?: string;
+    feedbackValidated?: string
 }
 
 /**
@@ -733,31 +734,31 @@ export interface UserAccount {
      * @type {string}
      * @memberof UserAccount
      */
-    pk?: string;
+    pk?: string
     /**
      * Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.
      * @type {string}
      * @memberof UserAccount
      */
-    username?: string;
+    username?: string
     /**
      *
      * @type {string}
      * @memberof UserAccount
      */
-    role?: UserAccount.RoleEnum;
+    role?: UserAccount.RoleEnum
     /**
      *
      * @type {boolean}
      * @memberof UserAccount
      */
-    isAdmin?: boolean;
+    isAdmin?: boolean
     /**
      *
      * @type {string}
      * @memberof UserAccount
      */
-    password?: string;
+    password?: string
 }
 
 /**
@@ -787,41 +788,41 @@ export interface VisibleCommentFeedback {
      * @type {number}
      * @memberof VisibleCommentFeedback
      */
-    pk?: number;
+    pk?: number
     /**
      *
      * @type {string}
      * @memberof VisibleCommentFeedback
      */
-    ofSubmission: string;
+    ofSubmission: string
     /**
      *
      * @type {boolean}
      * @memberof VisibleCommentFeedback
      */
-    isFinal?: boolean;
+    isFinal?: boolean
     /**
      *
      * @type {number}
      * @memberof VisibleCommentFeedback
      */
-    score?: number;
+    score?: number
     /**
      *
      * @type {string}
      * @memberof VisibleCommentFeedback
      */
-    feedbackLines?: string;
+    feedbackLines?: string
     /**
      *
      * @type {Date}
      * @memberof VisibleCommentFeedback
      */
-    created?: Date;
+    created?: Date
     /**
      *
      * @type {string}
      * @memberof VisibleCommentFeedback
      */
-    ofSubmissionType?: string;
+    ofSubmissionType?: string
 }
diff --git a/frontend/src/pages/student/StudentLayout.vue b/frontend/src/pages/student/StudentLayout.vue
index 6701470b..f8bc7d5b 100644
--- a/frontend/src/pages/student/StudentLayout.vue
+++ b/frontend/src/pages/student/StudentLayout.vue
@@ -2,7 +2,7 @@
   <base-layout>
 
     <template  slot="header">
-      {{ module_reference }}
+      {{ moduleReference }}
     </template>
 
     <v-list dense slot="sidebar-content">
@@ -60,7 +60,7 @@ export default {
   },
   computed: {
     ...mapState({
-      module_reference: state => state.studentPage.exam.module_reference,
+      moduleReference: state => state.studentPage.exam.moduleReference,
       submissions: state => state.studentPage.submissionsForList,
       exam: state => state.studentPage.exam,
       visited: state => state.studentPage.visited,
diff --git a/frontend/src/pages/student/StudentSubmissionPage.vue b/frontend/src/pages/student/StudentSubmissionPage.vue
index a0c3aea7..e6928c52 100644
--- a/frontend/src/pages/student/StudentSubmissionPage.vue
+++ b/frontend/src/pages/student/StudentSubmissionPage.vue
@@ -17,15 +17,15 @@
 
             <v-spacer/>
 
-            <h2>Score: {{feedback ? feedback.score : 'N/A'}} / {{submissionType.full_score}}</h2>
+            <h2>Score: {{feedback ? feedback.score : 'N/A'}} / {{submissionType.fullScore}}</h2>
           </v-toolbar>
           <template slot="table-content">
             <tr v-for="(code, lineNo) in submission" :key="lineNo">
               <submission-line :code="code" :lineNo="lineNo">
                 <template v-if="feedback">
-                  <template v-for="(comment, index) in feedback.feedback_lines[lineNo]">
+                  <template v-for="(comment, index) in feedback.feedbackLines[lineNo]">
                     <feedback-comment
-                      v-if="feedback.feedback_lines[lineNo] && showFeedback"
+                      v-if="feedback.feedbackLines[lineNo] && showFeedback"
                       v-bind="comment"
                       :line-no="lineNo"
                       :key="index"
diff --git a/frontend/src/store/getters.ts b/frontend/src/store/getters.ts
index 3cdc0cbe..12504862 100644
--- a/frontend/src/store/getters.ts
+++ b/frontend/src/store/getters.ts
@@ -1,7 +1,7 @@
 const getters = {
   corrected (state) {
-    return state.statistics.submission_type_progress.every(progress => {
-      return progress.feedback_final === progress.submission_count
+    return state.statistics.submissionTypeProgress.every(progress => {
+      return progress.feedbackFinal === progress.submissionCount
     })
   },
   getSubmission: state => pk => {
diff --git a/frontend/src/store/modules/authentication.ts b/frontend/src/store/modules/authentication.ts
index 9e0c95e7..c3a81187 100644
--- a/frontend/src/store/modules/authentication.ts
+++ b/frontend/src/store/modules/authentication.ts
@@ -17,7 +17,7 @@ interface AuthState {
         pk: string,
         username: string,
         role: string,
-        is_admin: boolean //eslint-disable-line
+        isAdmin: boolean
     }
 }
 function initialState (): AuthState {
@@ -31,7 +31,7 @@ function initialState (): AuthState {
       pk: '',
       username: '',
       role: '',
-      is_admin: false
+      isAdmin: false
     }
   }
 }
diff --git a/frontend/src/store/modules/feedback_list/feedback-table.ts b/frontend/src/store/modules/feedback_list/feedback-table.ts
index 4ea766a8..845fa13d 100644
--- a/frontend/src/store/modules/feedback_list/feedback-table.ts
+++ b/frontend/src/store/modules/feedback_list/feedback-table.ts
@@ -19,7 +19,7 @@ const feedbackTable = {
   state: initialState(),
   mutations: {
     [feedbackTableMut.SET_FEEDBACK_HISTORY]: (state, val) => {
-      state.feedbackHist = objectifyArray(val, 'of_submission')
+      state.feedbackHist = objectifyArray(val, 'ofSubmission')
     },
     [feedbackTableMut.ADD_ASSIGNMENTS_INFO]: (state, assignments) => {
       for (const assignment of assignments) {
@@ -28,20 +28,20 @@ const feedbackTable = {
           ...feedback.history,
           [assignment.stage]: {
             owner: assignment.owner,
-            is_done: assignment.is_done
+            isDone: assignment.isDone
           }
         }
       }
     },
     [feedbackTableMut.SET_FEEDBACK_OF_SUBMISSION_TYPE] (state, {feedback, type}) {
-      state.feedbackHist[feedback.of_submission].of_submission_type = type.name
+      state.feedbackHist[feedback.ofSubmission].ofSubmissionType = type.name
     },
     [feedbackTableMut.RESET_STATE]: (state) => { Object.assign(state, initialState()) }
   },
   actions: {
     mapFeedbackHistOfSubmissionType ({getters, commit, state}) {
       for (const feedback of Object.values(state.feedbackHist)) {
-        const type = getters.getSubmissionType((feedback as any).of_submission_type)
+        const type = getters.getSubmissionType((feedback as any).ofSubmissionType)
         commit(feedbackTableMut.SET_FEEDBACK_OF_SUBMISSION_TYPE, {feedback, type})
       }
     },
diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts
index cd02557f..f6619be2 100644
--- a/frontend/src/store/modules/submission-notes.ts
+++ b/frontend/src/store/modules/submission-notes.ts
@@ -35,11 +35,11 @@ function initialState () {
     origFeedback: {
       score: null,
       isFinal: false,
-      feedback_lines: {}
+      feedbackLines: {}
     },
     updatedFeedback: {
       score: null,
-      feedback_lines: {}
+      feedbackLines: {}
     },
     commentsMarkedForDeletion: {}
   }
@@ -58,7 +58,7 @@ const submissionNotes = {
     // this makes iterating over the submission much more pleasant
     submission: (state, getters) => {
       const language = getters.submissionType
-        ? getters.submissionType.programming_language
+        ? getters.submissionType.programmingLanguage
         : 'c'
       const highlighted = hljs.highlight(language, state.submission.text, true).value
       return highlighted.split('\n').reduce((acc, cur, index) => {
@@ -71,12 +71,12 @@ const submissionNotes = {
     },
     workInProgress: state => {
       const openEditor = Object.values(state.ui.showEditorOnLine).reduce((acc, curr) => acc || curr, false)
-      const feedbackWritten = Object.entries(state.updatedFeedback.feedback_lines).length > 0
+      const feedbackWritten = Object.entries(state.updatedFeedback.feedbackLines).length > 0
       return openEditor || feedbackWritten
     },
     isFeedbackCreation: state => {
-      return !state.origFeedback['feedback_stage_for_user'] ||
-        state.origFeedback['feedback_stage_for_user'] === 'feedback-creation'
+      return !state.origFeedback['feedbackStageForUser'] ||
+        state.origFeedback['feedbackStageForUser'] === 'feedback-creation'
     }
   },
   mutations: {
@@ -93,13 +93,13 @@ const submissionNotes = {
       state.ui.showFeedback = val
     },
     [subNotesMut.UPDATE_FEEDBACK_LINE]: function (state, feedback) {
-      Vue.set(state.updatedFeedback.feedback_lines, feedback.lineNo, feedback.comment)
+      Vue.set(state.updatedFeedback.feedbackLines, feedback.lineNo, feedback.comment)
     },
     [subNotesMut.UPDATE_FEEDBACK_SCORE]: function (state, score) {
       state.updatedFeedback.score = score
     },
     [subNotesMut.DELETE_FEEDBACK_LINE]: function (state, lineNo) {
-      Vue.delete(state.updatedFeedback.feedback_lines, lineNo)
+      Vue.delete(state.updatedFeedback.feedbackLines, lineNo)
     },
     [subNotesMut.TOGGLE_EDITOR_ON_LINE]: function (state, {lineNo, comment}) {
       Vue.set(state.ui.selectedCommentOnLine, lineNo, comment)
@@ -128,11 +128,11 @@ const submissionNotes = {
     },
     submitFeedback: async function ({state, dispatch, getters}, {isFinal = false}) {
       let feedback = {
-        is_final: isFinal,
-        of_submission: state.submission.pk
+        isFinal: isFinal,
+        ofSubmission: state.submission.pk
       }
-      if (Object.keys(state.updatedFeedback.feedback_lines).length > 0) {
-        feedback['feedback_lines'] = state.updatedFeedback.feedback_lines
+      if (Object.keys(state.updatedFeedback.feedbackLines).length > 0) {
+        feedback['feedbackLines'] = state.updatedFeedback.feedbackLines
       }
       if (state.origFeedback.score === null && state.updatedFeedback.score === null) {
         throw new Error('You need to give a score.')
diff --git a/frontend/src/store/modules/subscriptions.ts b/frontend/src/store/modules/subscriptions.ts
index 588867f0..79d7a1f8 100644
--- a/frontend/src/store/modules/subscriptions.ts
+++ b/frontend/src/store/modules/subscriptions.ts
@@ -1,6 +1,7 @@
 import Vue from 'vue'
 import * as api from '@/api'
-import {handleError, flatten, cartesian, once} from '@/util/helpers'
+import {cartesian, flatten, handleError, once} from '@/util/helpers'
+import {Subscription} from "@/models";
 
 export const subscriptionMuts = Object.freeze({
   SET_SUBSCRIPTIONS: 'SET_SUBSCRIPTIONS',
@@ -59,17 +60,17 @@ const subscriptions = {
       return state.subscriptions[state.activeSubscriptionPk]
     },
     resolveSubscriptionKeyToName: (state, getters, rootState) => subscription => {
-      switch (subscription.query_type) {
+      switch (subscription.queryType) {
         case 'random':
           return 'Active'
         case 'exam':
-          const examType = rootState.examTypes[subscription.query_key]
-          return examType ? examType.module_reference : 'Exam'
+          const examType = rootState.examTypes[subscription.queryKey]
+          return examType ? examType.moduleReference : 'Exam'
         case 'submission_type':
-          const submissionType = rootState.submissionTypes[subscription.query_key]
+          const submissionType = rootState.submissionTypes[subscription.queryKey]
           return submissionType ? submissionType.name : 'Submission Type'
         case 'student':
-          const studentName = rootState.students[subscription.query_key]
+          const studentName = rootState.students[subscription.queryKey]
           return studentName ? studentName.name : 'Student'
       }
     },
@@ -87,12 +88,12 @@ const subscriptions = {
         return acc
       }, {})
       Object.values(state.subscriptions).forEach((subscription: any) => {
-        subscriptionsByStage[subscription.feedback_stage][subscription.query_type].push(subscription)
+        subscriptionsByStage[subscription.feedbackStage][subscription.queryType].push(subscription)
       })
       // sort the resulting arrays in subscriptions lexicographically by their query_keys
       const sortSubscriptions = subscriptionsByType => Object.values(subscriptionsByType)
         .forEach((arr: object[]) => {
-          if (arr.length > 1 && arr[0].hasOwnProperty('query_key')) {
+          if (arr.length > 1 && arr[0].hasOwnProperty('queryKey')) {
             arr.sort((subA, subB) => {
               const subALower = getters.resolveSubscriptionKeyToName(subA).toLowerCase()
               const subBLower = getters.resolveSubscriptionKeyToName(subB).toLowerCase()
@@ -146,7 +147,7 @@ const subscriptions = {
           if (type === 'random') {
             return true
           }
-          return elem.query_key === key
+          return elem.queryKey === key
         })
         subscription = subscription || await api.subscribeTo(type, key, stage)
         commit(subscriptionMuts.SET_SUBSCRIPTION, subscription)
@@ -168,7 +169,8 @@ const subscriptions = {
     },
     /**
      * Creates as many assignments as needed to reach MAX_NUMBER_OF_ASSIGNMENTS
-     * @param numOfAssignments Use to override default behaviour of creating MAX_NUMBER_OF_ASSIGNMENTS - assignmentQueue.length assignments
+     * @param numOfAssignments Use to override default behaviour of
+     * creating MAX_NUMBER_OF_ASSIGNMENTS - assignmentQueue.length assignments
      * @returns {Promise<[Promise]>} returns Promise of Array of Promises of assignments
      */
     async getAssignmentsForActiveSubscription ({commit, state, dispatch, getters}, numOfAssignments) {
@@ -241,13 +243,13 @@ const subscriptions = {
       commit(subscriptionMuts.SET_ASSIGNMENT_QUEUE, [])
       commit(subscriptionMuts.SET_ACTIVE_SUBSCRIPTION_PK, '')
     },
-    async subscribeToType ({commit, state, dispatch, getters}, type) {
+    async subscribeToType ({commit, state, dispatch, getters}, type: Subscription.QueryTypeEnum) {
       switch (type) {
-        case 'random':
+        case Subscription.QueryTypeEnum.Random:
           return getters.availableStages.map(stage => {
             dispatch('subscribeTo', {type, stage})
           })
-        case 'exam':
+        case Subscription.QueryTypeEnum.Exam:
           if (getters.isReviewer) {
             const stageKeyCartesian = cartesian(
               getters.availableStages, getters.availableExamTypeQueryKeys)
@@ -256,7 +258,7 @@ const subscriptions = {
             })
           }
           return []
-        case 'submission_type':
+        case Subscription.QueryTypeEnum.SubmissionType:
           const stageKeyCartesian = cartesian(
             getters.availableStages, getters.availableSubmissionTypeQueryKeys)
           return stageKeyCartesian.map(([stage, key]) => {
diff --git a/frontend/src/store/mutations.ts b/frontend/src/store/mutations.ts
index 1c9e80ad..96fff066 100644
--- a/frontend/src/store/mutations.ts
+++ b/frontend/src/store/mutations.ts
@@ -57,7 +57,7 @@ const mutations = {
     Vue.set(state.feedback, feedback.pk, {
       ...state.feedback[feedback.pk],
       ...feedback,
-      of_submission_type: state.submissionTypes[feedback['of_submission_type']]
+      ofSubmissionType: state.submissionTypes[feedback['ofSubmissionType']]
     })
   },
   [mut.SET_STATISTICS] (state, statistics) {
@@ -68,9 +68,9 @@ const mutations = {
   },
   [mut.MAP_FEEDBACK_OF_SUBMISSION_TYPE] (state) {
     Object.values(state.feedback).forEach(feedback => {
-      const submissionType = state.submissionTypes[feedback['of_submission_type']]
+      const submissionType = state.submissionTypes[feedback['ofSubmissionType']]
       if (submissionType) {
-        feedback['of_submission_type'] = submissionType
+        feedback['ofSubmissionType'] = submissionType
       }
     })
   },
@@ -96,7 +96,7 @@ function mapStudent (student, map) {
   if (Object.keys(map).length > 0) {
     return {
       ...student,
-      ...map[student.matrikel_no]
+      ...map[student.matrikelNo]
     }
   }
   return student
diff --git a/frontend/src/store/store.ts b/frontend/src/store/store.ts
index 9b00fccb..db8c0a28 100644
--- a/frontend/src/store/store.ts
+++ b/frontend/src/store/store.ts
@@ -17,6 +17,7 @@ import {lastInteraction} from '@/store/plugins/lastInteractionPlugin'
 
 Vue.use(Vuex)
 
+
 export function initialState () {
   return {
     lastAppInteraction: Date.now(),
@@ -27,7 +28,7 @@ export function initialState () {
     students: {},
     studentMap: {}, // is used to map obfuscated student data back to the original
     statistics: {
-      submission_type_progress: []
+      submissionTypeProgress: []
     },
     tutors: []
   }
-- 
GitLab


From b95340f98bb423fcf019652c3df69469fd652e83 Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Mon, 13 Aug 2018 00:01:40 +0200
Subject: [PATCH 4/9] Soo so many added typings...

Nearly completed typing the whole store!
---
 core/urls.py                                  |   3 +-
 frontend/package.json                         |   1 +
 frontend/src/api.ts                           |  20 +-
 .../submission_notes/SubmissionCorrection.vue |   1 -
 frontend/src/models.ts                        |  48 +-
 frontend/src/store/actions.ts                 |  15 +-
 frontend/src/store/getters.ts                 |  12 +-
 frontend/src/store/modules/authentication.ts  |  15 +-
 .../feedback_list/feedback-search-options.ts  |  18 +-
 .../modules/feedback_list/feedback-table.ts   |  50 +-
 frontend/src/store/modules/student-page.ts    |  25 +-
 .../src/store/modules/submission-notes.ts     |  68 +-
 frontend/src/store/modules/subscriptions.ts   |  89 ++-
 frontend/src/store/modules/ui.ts              |  21 +-
 frontend/src/store/mutations.ts               |  53 +-
 .../store/plugins/lastInteractionPlugin.ts    |   5 +-
 frontend/src/store/store.ts                   |  23 +-
 frontend/src/util/helpers.ts                  |  34 +-
 frontend/yarn.lock                            | 691 +-----------------
 grady/settings/default.py                     |   9 +-
 requirements.txt                              |   2 +
 21 files changed, 325 insertions(+), 878 deletions(-)

diff --git a/core/urls.py b/core/urls.py
index 3e3014d9..a042f5f9 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -2,10 +2,9 @@ from django.urls import path, re_path
 from drf_yasg.views import get_schema_view
 from drf_yasg import openapi
 from rest_framework.routers import DefaultRouter
-from rest_framework.permissions import IsAdminUser, AllowAny
+from rest_framework.permissions import AllowAny
 
 from core import views
-from core.permissions import IsReviewer
 
 # Create a router and register our viewsets with it.
 router = DefaultRouter()
diff --git a/frontend/package.json b/frontend/package.json
index ab0d228b..3eca1380 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -26,6 +26,7 @@
   },
   "devDependencies": {
     "@types/chai": "^4.1.0",
+    "@types/highlight.js": "^9.12.3",
     "@types/mocha": "^5.2.4",
     "@vue/cli-plugin-e2e-nightwatch": "^3.0.0-rc.10",
     "@vue/cli-plugin-eslint": "^3.0.0-rc.10",
diff --git a/frontend/src/api.ts b/frontend/src/api.ts
index a50422e5..1f2d2c17 100644
--- a/frontend/src/api.ts
+++ b/frontend/src/api.ts
@@ -1,4 +1,4 @@
-import axios, {AxiosInstance, AxiosPromise, AxiosResponse} from 'axios'
+import axios, {AxiosInstance, AxiosResponse} from 'axios'
 import {Credentials} from '@/store/modules/authentication'
 import {
   Assignment,
@@ -46,7 +46,7 @@ export async function fetchJWT (credentials: Credentials): Promise<JSONWebToken>
 }
 
 export async function refreshJWT (oldToken: string): Promise<JSONWebToken> {
-  const token: string = (await ax.post('/api/refresh-oldToken/', {oldToken})).data.token
+  const token: string = (await ax.post('/api/refresh-token/', {oldToken})).data.token
   ax.defaults.headers['Authorization'] = `JWT ${token}`
   return {token}
 }
@@ -134,10 +134,16 @@ export async function fetchStatistics (opt = {fields: []}): Promise<Statistics>
   return (await ax.get(url)).data
 }
 
+interface SubscriptionCreatePayload {
+    queryType: Subscription.QueryTypeEnum
+    queryKey?: string
+    feedbackStage?: Subscription.FeedbackStageEnum
+}
+
 export async function subscribeTo (type: Subscription.QueryTypeEnum,
-  key: string,
-  stage: Subscription.FeedbackStageEnum): Promise<Subscription> {
-  let data: Subscription = {
+  key?: string,
+  stage?: Subscription.FeedbackStageEnum): Promise<Subscription> {
+  let data: SubscriptionCreatePayload = {
     queryType: type
   }
 
@@ -160,7 +166,7 @@ export async function createAssignment (
   return (await ax.post(`/api/assignment/`, data)).data
 }
 
-export async function submitFeedbackForAssignment ({feedback}: {feedback: Feedback}): Promise<Feedback> {
+export async function submitFeedbackForAssignment ({feedback}: {feedback: Partial<Feedback>}): Promise<Feedback> {
   return (await ax.post('/api/feedback/', feedback)).data
 }
 
@@ -168,7 +174,7 @@ export async function submitUpdatedFeedback ({feedback}: {feedback: Feedback}):
   return (await ax.patch(`/api/feedback/${feedback.ofSubmission}/`, feedback)).data
 }
 
-export async function fetchSubmissionTypes (fields = []): Promise<Array<SubmissionType>> {
+export async function fetchSubmissionTypes (fields: Array<string> = []): Promise<Array<SubmissionType>> {
   let url = '/api/submissiontype/'
   if (fields.length > 0) {
     url += '?fields=pk,' + fields
diff --git a/frontend/src/components/submission_notes/SubmissionCorrection.vue b/frontend/src/components/submission_notes/SubmissionCorrection.vue
index 3c70cffe..d7b5c17f 100644
--- a/frontend/src/components/submission_notes/SubmissionCorrection.vue
+++ b/frontend/src/components/submission_notes/SubmissionCorrection.vue
@@ -108,7 +108,6 @@ export default {
       'isTutor',
       'isReviewer',
       'getSubmission',
-      'getFeedback',
       'getSubmissionType',
       'workInProgress'
     ]),
diff --git a/frontend/src/models.ts b/frontend/src/models.ts
index 87153cb7..c5f59b16 100644
--- a/frontend/src/models.ts
+++ b/frontend/src/models.ts
@@ -9,7 +9,7 @@ export interface Assignment {
      * @type {string}
      * @memberof Assignment
      */
-    pk?: string
+    pk: string
     /**
      *
      * @type {string}
@@ -33,7 +33,11 @@ export interface Assignment {
      * @type {string}
      * @memberof Assignment
      */
-    stage?: string
+    stage?: Subscription.FeedbackStageEnum
+
+    feedback?: Feedback
+
+    subscription?: string
 }
 
 /**
@@ -47,7 +51,7 @@ export interface Exam {
      * @type {string}
      * @memberof Exam
      */
-    pk?: string
+    pk: string
     /**
      *
      * @type {string}
@@ -85,13 +89,13 @@ export interface Feedback {
      * @type {number}
      * @memberof Feedback
      */
-    pk?: number
+    pk: number
     /**
      *
      * @type {string}
      * @memberof Feedback
      */
-    ofSubmission: string
+    ofSubmission?: string
     /**
      *
      * @type {boolean}
@@ -141,7 +145,7 @@ export interface FeedbackComment {
      * @type {string}
      * @memberof FeedbackComment
      */
-    pk?: string
+    pk: string
     /**
      *
      * @type {string}
@@ -217,7 +221,7 @@ export interface StudentInfo {
      * @type {string}
      * @memberof StudentInfo
      */
-    pk?: string
+    pk: string
     /**
      *
      * @type {string}
@@ -267,7 +271,7 @@ export interface StudentInfoForListView {
      * @type {string}
      * @memberof StudentInfoForListView
      */
-    pk?: string
+    pk: string
     /**
      *
      * @type {string}
@@ -329,7 +333,7 @@ export interface Submission {
      * @type {string}
      * @memberof Submission
      */
-    pk?: string
+    pk: string
     /**
      *
      * @type {SubmissionType}
@@ -367,7 +371,7 @@ export interface SubmissionList {
      * @type {string}
      * @memberof SubmissionList
      */
-    pk?: string
+    pk: string
     /**
      *
      * @type {SubmissionTypeList}
@@ -393,7 +397,7 @@ export interface SubmissionNoTextFields {
      * @type {string}
      * @memberof SubmissionNoTextFields
      */
-    pk?: string
+    pk: string
     /**
      *
      * @type {string}
@@ -431,7 +435,7 @@ export interface SubmissionNoType {
      * @type {string}
      * @memberof SubmissionNoType
      */
-    pk?: string
+    pk: string
     /**
      *
      * @type {string}
@@ -455,7 +459,7 @@ export interface SubmissionNoType {
      * @type {Feedback}
      * @memberof SubmissionNoType
      */
-    feedback: Feedback
+    feedback?: Feedback
     /**
      *
      * @type {Array<Test>}
@@ -475,7 +479,7 @@ export interface SubmissionType {
      * @type {string}
      * @memberof SubmissionType
      */
-    pk?: string
+    pk: string
     /**
      *
      * @type {string}
@@ -534,7 +538,7 @@ export interface SubmissionTypeList {
      * @type {string}
      * @memberof SubmissionTypeList
      */
-    pk?: string
+    pk: string
 
     /**
      *
@@ -551,7 +555,7 @@ export interface SubmissionTypeList {
 }
 
 export interface SubmissionTypeProgress {
-    pk?: string
+    pk: string
     name: string
     feedbackFinal: number
     feedbackInValidation: number
@@ -570,7 +574,7 @@ export interface Subscription {
      * @type {string}
      * @memberof Subscription
      */
-    pk?: string
+    pk: string
     /**
      *
      * @type {string}
@@ -606,7 +610,7 @@ export interface Subscription {
      * @type {string}
      * @memberof Subscription
      */
-    assignments?: string
+    assignments?: Array<Assignment>
     /**
      *
      * @type {string}
@@ -658,7 +662,7 @@ export interface Test {
      * @type {string}
      * @memberof Test
      */
-    pk?: string
+    pk: string
     /**
      *
      * @type {string}
@@ -690,7 +694,7 @@ export interface Tutor {
      * @type {string}
      * @memberof Tutor
      */
-    pk?: string
+    pk: string
     /**
      *
      * @type {string}
@@ -734,7 +738,7 @@ export interface UserAccount {
      * @type {string}
      * @memberof UserAccount
      */
-    pk?: string
+    pk: string
     /**
      * Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.
      * @type {string}
@@ -788,7 +792,7 @@ export interface VisibleCommentFeedback {
      * @type {number}
      * @memberof VisibleCommentFeedback
      */
-    pk?: number
+    pk: number
     /**
      *
      * @type {string}
diff --git a/frontend/src/store/actions.ts b/frontend/src/store/actions.ts
index 47ca46fc..24babbe0 100644
--- a/frontend/src/store/actions.ts
+++ b/frontend/src/store/actions.ts
@@ -4,8 +4,10 @@ import { subNotesMut } from '@/store/modules/submission-notes'
 import * as api from '@/api'
 import router from '@/router/index'
 import { handleError } from '@/util/helpers'
+import {ActionTree} from 'vuex'
+import {RootState} from '@/store/store'
 
-const actions = {
+const actions: ActionTree<RootState, RootState> = {
   async getExamTypes ({commit, dispatch}) {
     try {
       const examTypes = await api.fetchExamType({})
@@ -14,7 +16,7 @@ const actions = {
       handleError(err, dispatch, 'Unable to fetch exam types')
     }
   },
-  async updateSubmissionTypes ({commit, dispatch}, fields = []) {
+  async updateSubmissionTypes ({commit, dispatch}, fields: Array<string> = []) {
     try {
       const submissionTypes = await api.fetchSubmissionTypes(fields)
       submissionTypes.forEach(type => {
@@ -24,7 +26,9 @@ const actions = {
       handleError(err, dispatch, 'Unable to get submission types')
     }
   },
-  async getStudents ({commit, dispatch}, opt = {studentPks: [], fields: []}) {
+  async getStudents ({commit, dispatch},
+    opt: {studentPks: Array<string>, fields: Array<string>} =
+    {studentPks: [], fields: []}) {
     try {
       if (opt.studentPks.length === 0) {
         const students = await api.fetchAllStudents()
@@ -32,7 +36,7 @@ const actions = {
         return students
       } else {
         const students = await Promise.all(
-          opt.studentPks.map(pk => api.fetchStudent({
+          opt.studentPks.map((pk: string) => api.fetchStudent({
             pk,
             fields: opt.fields
           }))
@@ -104,7 +108,8 @@ const actions = {
   logout ({ commit, getters, state }, message = '') {
     if (getters.isStudent) {
       // change active to false when user logs out
-      api.changeActiveForUser(state.authentication.user.pk, false)
+      // TODO this should belong in auth module
+      api.changeActiveForUser((state as any).authentication.user.pk, false)
     }
     commit('RESET_STATE')
     commit('submissionNotes/' + subNotesMut.RESET_STATE)
diff --git a/frontend/src/store/getters.ts b/frontend/src/store/getters.ts
index 12504862..44e8a56d 100644
--- a/frontend/src/store/getters.ts
+++ b/frontend/src/store/getters.ts
@@ -1,16 +1,16 @@
-const getters = {
+import {RootState} from '@/store/store'
+import {GetterTree} from 'vuex'
+
+const getters: GetterTree<RootState, RootState> = {
   corrected (state) {
     return state.statistics.submissionTypeProgress.every(progress => {
       return progress.feedbackFinal === progress.submissionCount
     })
   },
-  getSubmission: state => pk => {
+  getSubmission: state => (pk: string) => {
     return state.submissions[pk]
   },
-  getFeedback: state => pk => {
-    return state.feedback[pk]
-  },
-  getSubmissionType: state => pk => {
+  getSubmissionType: state => (pk: string) => {
     return state.submissionTypes[pk]
   }
 }
diff --git a/frontend/src/store/modules/authentication.ts b/frontend/src/store/modules/authentication.ts
index c3a81187..1b8381f5 100644
--- a/frontend/src/store/modules/authentication.ts
+++ b/frontend/src/store/modules/authentication.ts
@@ -1,6 +1,7 @@
 import * as api from '@/api'
 import gradySays from '../grady_speak'
 import {ActionContext, Module} from 'vuex'
+import {UserAccount} from '@/models'
 
 export interface Credentials {
     username: string,
@@ -13,12 +14,7 @@ interface AuthState {
     refreshingToken: boolean,
     jwtTimeDelta: number,
     message: string,
-    user: {
-        pk: string,
-        username: string,
-        role: string,
-        isAdmin: boolean
-    }
+    user: UserAccount
 }
 function initialState (): AuthState {
   return {
@@ -30,7 +26,6 @@ function initialState (): AuthState {
     user: {
       pk: '',
       username: '',
-      role: '',
       isAdmin: false
     }
   }
@@ -53,13 +48,13 @@ const authentication: Module<AuthState, any> = {
       return gradySays[Math.floor(Math.random() * gradySays.length)]
     },
     isStudent: (state: AuthState) => {
-      return state.user.role === 'Student'
+      return state.user.role === UserAccount.RoleEnum.Student
     },
     isTutor: (state: AuthState) => {
-      return state.user.role === 'Tutor'
+      return state.user.role === UserAccount.RoleEnum.Tutor
     },
     isReviewer: (state: AuthState) => {
-      return state.user.role === 'Reviewer'
+      return state.user.role === UserAccount.RoleEnum.Reviewer
     },
     isTutorOrReviewer: (state: AuthState, getters) => {
       return getters.isTutor || getters.isReviewer
diff --git a/frontend/src/store/modules/feedback_list/feedback-search-options.ts b/frontend/src/store/modules/feedback_list/feedback-search-options.ts
index c79120eb..4b89087f 100644
--- a/frontend/src/store/modules/feedback_list/feedback-search-options.ts
+++ b/frontend/src/store/modules/feedback_list/feedback-search-options.ts
@@ -1,3 +1,6 @@
+import {Module} from 'vuex'
+import {RootState} from '@/store/store'
+
 export const namespace = 'feedbackSearchOptions'
 
 export const feedbackSearchOptsMut = Object.freeze({
@@ -9,7 +12,16 @@ export const feedbackSearchOptsMut = Object.freeze({
   SET_FILTER_BY_STAGE: 'SET_FILTER_BY_STAGE'
 })
 
-function initialState () {
+interface FeedbackSearchOptionsState {
+  showFinal: boolean
+  searchOtherUserComments: boolean
+  caseSensitive: boolean
+  useRegex: boolean
+  filterByTutors: string[]
+  filterByStage: string
+}
+
+function initialState (): FeedbackSearchOptionsState {
   return {
     showFinal: false,
     searchOtherUserComments: false,
@@ -20,12 +32,12 @@ function initialState () {
   }
 }
 
-const mapStageDisplayToApiString = {
+const mapStageDisplayToApiString: {[index: string]: string} = {
   'Initial feedback': 'feedback-creation',
   'Validation': 'feedback-validation'
 }
 
-const feedbackSearchOptions = {
+const feedbackSearchOptions: Module<FeedbackSearchOptionsState, RootState> = {
   namespaced: true,
   state: initialState(),
   getters: {
diff --git a/frontend/src/store/modules/feedback_list/feedback-table.ts b/frontend/src/store/modules/feedback_list/feedback-table.ts
index 845fa13d..f005582e 100644
--- a/frontend/src/store/modules/feedback_list/feedback-table.ts
+++ b/frontend/src/store/modules/feedback_list/feedback-table.ts
@@ -1,5 +1,8 @@
 import {fetchAllFeedback, fetchAllAssignments} from '@/api'
 import {objectifyArray} from '@/util/helpers'
+import {Assignment, Feedback, Subscription} from '@/models'
+import {Module} from 'vuex'
+import {RootState} from '@/store/store'
 
 export const feedbackTableMut = Object.freeze({
   SET_FEEDBACK_HISTORY: 'SET_FEEDBACK_HISTORY',
@@ -9,20 +12,36 @@ export const feedbackTableMut = Object.freeze({
   RESET_STATE: 'RESET_STATE'
 })
 
-function initialState () {
+export interface FeedbackHistoryItem extends Feedback {
+  history?: {
+    [key in Subscription.FeedbackStageEnum]: {
+      owner: string
+      isDone: boolean
+    }
+  }
+}
+
+interface FeedbackTableState {
+    feedbackHist: {[submissionPk: string]: FeedbackHistoryItem}
+}
+
+function initialState (): FeedbackTableState {
   return {
     feedbackHist: {}
   }
 }
 
-const feedbackTable = {
+const feedbackTable: Module<FeedbackTableState, RootState> = {
   state: initialState(),
   mutations: {
-    [feedbackTableMut.SET_FEEDBACK_HISTORY]: (state, val) => {
+    [feedbackTableMut.SET_FEEDBACK_HISTORY]: (state, val: Array<Feedback>) => {
       state.feedbackHist = objectifyArray(val, 'ofSubmission')
     },
-    [feedbackTableMut.ADD_ASSIGNMENTS_INFO]: (state, assignments) => {
+    [feedbackTableMut.ADD_ASSIGNMENTS_INFO]: (state, assignments: Array<Assignment>) => {
       for (const assignment of assignments) {
+        if (!assignment.submission || !assignment.stage) {
+          throw Error()
+        }
         const feedback = state.feedbackHist[assignment.submission]
         feedback.history = {
           ...feedback.history,
@@ -46,18 +65,17 @@ const feedbackTable = {
       }
     },
     async getFeedbackHistory ({getters, commit, dispatch}) {
-      let data = []
-      data.push(fetchAllFeedback())
-      if (getters.isReviewer) {
-        data.push(fetchAllAssignments())
-      }
-      Promise.all(data).then(([feedback, assignments]) => {
-        commit(feedbackTableMut.SET_FEEDBACK_HISTORY, feedback)
-        dispatch('mapFeedbackHistOfSubmissionType')
-        if (assignments) {
-          commit(feedbackTableMut.ADD_ASSIGNMENTS_INFO, assignments)
-        }
-      })
+      let data: [Promise<Feedback[]>, Promise<Assignment[]> | undefined] =
+          [fetchAllFeedback(), getters.isReviewer() ? fetchAllAssignments() : undefined]
+
+      Promise.all<Feedback[], Assignment[] | undefined>(data)
+        .then(([feedbacks, assignments]: [Feedback[], Assignment[]?]) => {
+          commit(feedbackTableMut.SET_FEEDBACK_HISTORY, feedbacks)
+          dispatch('mapFeedbackHistOfSubmissionType')
+          if (assignments) {
+            commit(feedbackTableMut.ADD_ASSIGNMENTS_INFO, assignments)
+          }
+        })
     }
   }
 }
diff --git a/frontend/src/store/modules/student-page.ts b/frontend/src/store/modules/student-page.ts
index f676cf4f..5125c7e4 100644
--- a/frontend/src/store/modules/student-page.ts
+++ b/frontend/src/store/modules/student-page.ts
@@ -1,9 +1,21 @@
 import {fetchStudentSelfData, fetchStudentSubmissions} from '@/api'
+import {Exam, Submission, SubmissionList} from '@/models'
+import {RootState} from '@/store/store'
+import {Module} from 'vuex'
 
-function initialState () {
+interface StudentPageState {
+  studentName: string
+  exam?: Exam
+  submissionsForList: Array<SubmissionList>
+  submissionData: {[typePk: string]: Submission}
+  visited: {[typePk: string]: boolean}
+  loaded: boolean
+}
+
+function initialState (): StudentPageState {
   return {
     studentName: '',
-    exam: {},
+    exam: undefined,
     submissionsForList: [],
     submissionData: {},
     visited: {},
@@ -22,7 +34,7 @@ export const studentPageMut = Object.freeze({
   RESET_STATE: 'RESET_STATE'
 })
 
-const studentPage = {
+const studentPage: Module<StudentPageState, RootState> = {
   state: initialState(),
   mutations: {
     [studentPageMut.SET_STUDENT_NAME] (state, name) {
@@ -31,9 +43,6 @@ const studentPage = {
     [studentPageMut.SET_EXAM] (state, exam) {
       state.exam = exam
     },
-    [studentPageMut.SET_SUBMISSION_TYPES] (state, submissionTypes) {
-      state.submissionTypes = submissionTypes
-    },
     [studentPageMut.SET_SUBMISSIONS_FOR_LIST] (state, submissions) {
       state.submissionsForList = submissions
     },
@@ -43,8 +52,8 @@ const studentPage = {
      * the former array elements. This is done to have direct access to the data
      * via the SubmissionType id.
      */
-    [studentPageMut.SET_FULL_SUBMISSION_DATA] (state, submissionData) {
-      state.submissionData = submissionData.reduce((acc, cur, index) => {
+    [studentPageMut.SET_FULL_SUBMISSION_DATA] (state, submissionData: Array<Submission>) {
+      state.submissionData = submissionData.reduce((acc: {[pk: string]: Submission}, cur) => {
         acc[cur.type.pk] = cur
         return acc
       }, {})
diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts
index f6619be2..32a74341 100644
--- a/frontend/src/store/modules/submission-notes.ts
+++ b/frontend/src/store/modules/submission-notes.ts
@@ -1,7 +1,10 @@
 import Vue from 'vue'
-import hljs from 'highlight.js'
+import * as hljs from 'highlight.js'
 import * as api from '@/api'
 import {nameSpacer} from '@/util/helpers'
+import {Feedback, FeedbackComment, Submission, SubmissionNoType} from '@/models'
+import {RootState} from '@/store/store'
+import {Module} from 'vuex'
 
 export const subNotesNamespace = nameSpacer('submissionNotes/')
 
@@ -19,12 +22,26 @@ export const subNotesMut = Object.freeze({
   RESET_STATE: 'RESET_STATE'
 })
 
-function initialState () {
+interface SubmissionNotesState {
+  submission: SubmissionNoType
+  ui: {
+      showEditorOnLine: {[lineNo: number]: boolean}
+      selectedCommentOnLine: {[lineNo: number]: FeedbackComment}
+      showFeedback: boolean
+  },
+  hasOrigFeedback: boolean
+  origFeedback: Feedback
+  updatedFeedback: Feedback
+  commentsMarkedForDeletion: {[pk: string]: FeedbackComment}
+}
+
+function initialState (): SubmissionNotesState {
   return {
-    assignment: '',
     submission: {
       text: '',
-      pk: ''
+      pk: '',
+      type: '',
+      tests: []
     },
     ui: {
       showEditorOnLine: {},
@@ -33,19 +50,21 @@ function initialState () {
     },
     hasOrigFeedback: false,
     origFeedback: {
-      score: null,
+      pk: 0,
+      score: 0,
       isFinal: false,
       feedbackLines: {}
     },
     updatedFeedback: {
-      score: null,
+      pk: 0,
+      score: 0,
       feedbackLines: {}
     },
     commentsMarkedForDeletion: {}
   }
 }
 
-const submissionNotes = {
+const submissionNotes: Module<SubmissionNotesState, RootState> = {
   namespaced: true,
   state: initialState(),
   getters: {
@@ -60,8 +79,8 @@ const submissionNotes = {
       const language = getters.submissionType
         ? getters.submissionType.programmingLanguage
         : 'c'
-      const highlighted = hljs.highlight(language, state.submission.text, true).value
-      return highlighted.split('\n').reduce((acc, cur, index) => {
+      const highlighted = hljs.highlight(language, state.submission.text || '', true).value
+      return highlighted.split('\n').reduce((acc: {[k: number]: string}, cur, index) => {
         acc[index + 1] = cur
         return acc
       }, {})
@@ -71,7 +90,7 @@ const submissionNotes = {
     },
     workInProgress: state => {
       const openEditor = Object.values(state.ui.showEditorOnLine).reduce((acc, curr) => acc || curr, false)
-      const feedbackWritten = Object.entries(state.updatedFeedback.feedbackLines).length > 0
+      const feedbackWritten = Object.entries(state.updatedFeedback.feedbackLines || {}).length > 0
       return openEditor || feedbackWritten
     },
     isFeedbackCreation: state => {
@@ -80,26 +99,31 @@ const submissionNotes = {
     }
   },
   mutations: {
-    [subNotesMut.SET_SUBMISSION]: function (state, submission) {
+    [subNotesMut.SET_SUBMISSION]: function (state, submission: SubmissionNoType) {
       state.submission = submission
     },
-    [subNotesMut.SET_ORIG_FEEDBACK]: function (state, feedback) {
+    [subNotesMut.SET_ORIG_FEEDBACK]: function (state, feedback: Feedback) {
       if (feedback) {
         state.origFeedback = feedback
         state.hasOrigFeedback = true
       }
     },
-    [subNotesMut.SET_SHOW_FEEDBACK]: function (state, val) {
+    [subNotesMut.SET_SHOW_FEEDBACK]: function (state, val: boolean) {
       state.ui.showFeedback = val
     },
-    [subNotesMut.UPDATE_FEEDBACK_LINE]: function (state, feedback) {
-      Vue.set(state.updatedFeedback.feedbackLines, feedback.lineNo, feedback.comment)
+    [subNotesMut.UPDATE_FEEDBACK_LINE]: function (state, feedback: {lineNo: number, comment: Partial<FeedbackComment>}) {
+      // explicit .toString() on lineNo is necessary because of Vue.set typings
+      if (state.updatedFeedback.feedbackLines) {
+          Vue.set(state.updatedFeedback.feedbackLines, feedback.lineNo.toString(), feedback.comment)
+      }
     },
     [subNotesMut.UPDATE_FEEDBACK_SCORE]: function (state, score) {
       state.updatedFeedback.score = score
     },
     [subNotesMut.DELETE_FEEDBACK_LINE]: function (state, lineNo) {
-      Vue.delete(state.updatedFeedback.feedbackLines, lineNo)
+      if (state.updatedFeedback.feedbackLines) {
+          Vue.delete(state.updatedFeedback.feedbackLines, lineNo)
+      }
     },
     [subNotesMut.TOGGLE_EDITOR_ON_LINE]: function (state, {lineNo, comment}) {
       Vue.set(state.ui.selectedCommentOnLine, lineNo, comment)
@@ -127,24 +151,24 @@ const submissionNotes = {
       )
     },
     submitFeedback: async function ({state, dispatch, getters}, {isFinal = false}) {
-      let feedback = {
+      let feedback: Partial<Feedback> = {
         isFinal: isFinal,
         ofSubmission: state.submission.pk
       }
-      if (Object.keys(state.updatedFeedback.feedbackLines).length > 0) {
-        feedback['feedbackLines'] = state.updatedFeedback.feedbackLines
+      if (Object.keys(state.updatedFeedback.feedbackLines || {}).length > 0) {
+        feedback.feedbackLines = state.updatedFeedback.feedbackLines
       }
       if (state.origFeedback.score === null && state.updatedFeedback.score === null) {
         throw new Error('You need to give a score.')
       } else if (state.updatedFeedback.score !== null) {
-        feedback['score'] = state.updatedFeedback.score
+        feedback.score = state.updatedFeedback.score
       }
       await dispatch('deleteComments')
       if (!state.hasOrigFeedback) {
         return api.submitFeedbackForAssignment({feedback})
       } else {
-        feedback['pk']= state.origFeedback.pk
-        return api.submitUpdatedFeedback({feedback})
+        feedback.pk = state.origFeedback.pk
+        return api.submitUpdatedFeedback(<{feedback: Feedback}> {feedback})
       }
     }
   }
diff --git a/frontend/src/store/modules/subscriptions.ts b/frontend/src/store/modules/subscriptions.ts
index 79d7a1f8..dab0467f 100644
--- a/frontend/src/store/modules/subscriptions.ts
+++ b/frontend/src/store/modules/subscriptions.ts
@@ -1,7 +1,9 @@
 import Vue from 'vue'
 import * as api from '@/api'
 import {cartesian, flatten, handleError, once} from '@/util/helpers'
-import {Subscription} from "@/models";
+import {Assignment, Subscription} from '@/models'
+import {ActionContext, Module} from 'vuex'
+import {RootState} from '@/store/store'
 
 export const subscriptionMuts = Object.freeze({
   SET_SUBSCRIPTIONS: 'SET_SUBSCRIPTIONS',
@@ -14,7 +16,14 @@ export const subscriptionMuts = Object.freeze({
   RESET_STATE: 'RESET_STATE'
 })
 
-function initialState () {
+interface SubscriptionsState {
+  subscriptions: {[pk: string]: Subscription}
+  assignmentQueue: Array<Assignment>
+  activeSubscriptionPk: string
+  loading: boolean
+}
+
+function initialState (): SubscriptionsState {
   return {
     subscriptions: {},
     assignmentQueue: [],
@@ -26,7 +35,7 @@ function initialState () {
 const MAX_NUMBER_OF_ASSIGNMENTS = 2
 
 // noinspection JSCommentMatchesSignature
-const subscriptions = {
+const subscriptions: Module<SubscriptionsState, RootState> = {
   state: initialState(),
   getters: {
     availableTypes (state, getters) {
@@ -59,21 +68,22 @@ const subscriptions = {
     activeSubscription (state) {
       return state.subscriptions[state.activeSubscriptionPk]
     },
-    resolveSubscriptionKeyToName: (state, getters, rootState) => subscription => {
+    resolveSubscriptionKeyToName: (state, getters, rootState) => (subscription: Subscription) => {
       switch (subscription.queryType) {
-        case 'random':
+        case Subscription.QueryTypeEnum.Random:
           return 'Active'
-        case 'exam':
-          const examType = rootState.examTypes[subscription.queryKey]
-          return examType ? examType.moduleReference : 'Exam'
-        case 'submission_type':
-          const submissionType = rootState.submissionTypes[subscription.queryKey]
-          return submissionType ? submissionType.name : 'Submission Type'
-        case 'student':
-          const studentName = rootState.students[subscription.queryKey]
-          return studentName ? studentName.name : 'Student'
+        case Subscription.QueryTypeEnum.Exam:
+          return subscription.queryKey
+            ? rootState.examTypes[subscription.queryKey].moduleReference : 'Exam'
+        case Subscription.QueryTypeEnum.SubmissionType:
+          return subscription.queryKey
+            ? rootState.submissionTypes[subscription.queryKey].name : 'Submission Type'
+        case Subscription.QueryTypeEnum.Student:
+          return subscription.queryKey
+            ? rootState.students[subscription.queryKey].name : 'Student'
       }
     },
+    // TODO Refactor this monstrosity
     getSubscriptionsGroupedByType (state, getters) {
       const subscriptionsByType = () => {
         return {
@@ -114,37 +124,40 @@ const subscriptions = {
     }
   },
   mutations: {
-    [subscriptionMuts.SET_SUBSCRIPTIONS] (state, subscriptions) {
-      state.subscriptions = subscriptions.reduce((acc, curr) => {
-        acc[curr['pk']] = curr
+    [subscriptionMuts.SET_SUBSCRIPTIONS] (state, subscriptions: Array<Subscription>): void {
+      state.subscriptions = subscriptions.reduce((acc: {[pk: string]: Subscription}, curr) => {
+        acc[curr.pk] = curr
         return acc
       }, {})
     },
-    [subscriptionMuts.SET_SUBSCRIPTION] (state, subscription) {
+    [subscriptionMuts.SET_SUBSCRIPTION] (state, subscription: Subscription): void {
       Vue.set(state.subscriptions, subscription.pk, subscription)
     },
-    [subscriptionMuts.SET_ACTIVE_SUBSCRIPTION_PK] (state, subscriptionPk) {
+    [subscriptionMuts.SET_ACTIVE_SUBSCRIPTION_PK] (state, subscriptionPk: string): void {
       state.activeSubscriptionPk = subscriptionPk
     },
-    [subscriptionMuts.SET_ASSIGNMENT_QUEUE] (state, queue) {
+    [subscriptionMuts.SET_ASSIGNMENT_QUEUE] (state, queue: Array<Assignment>): void {
       state.assignmentQueue = queue
     },
-    [subscriptionMuts.ADD_ASSIGNMENT_TO_QUEUE] (state, assignment) {
+    [subscriptionMuts.ADD_ASSIGNMENT_TO_QUEUE] (state, assignment: Assignment): void {
       state.assignmentQueue.push(assignment)
     },
-    [subscriptionMuts.POP_ASSIGNMENT_FROM_QUEUE] (state) {
+    [subscriptionMuts.POP_ASSIGNMENT_FROM_QUEUE] (state): void {
       state.assignmentQueue.shift()
     },
-    [subscriptionMuts.RESET_STATE] (state) {
+    [subscriptionMuts.RESET_STATE] (state): void {
       Object.assign(state, initialState())
     }
   },
   actions: {
-    subscribeTo: async function ({commit, dispatch, getters}, {type, key, stage}) {
+    subscribeTo: async function (
+      {commit, dispatch, getters},
+      {type, key, stage}:
+      {type: Subscription.QueryTypeEnum, key?: string, stage: Subscription.FeedbackStageEnum}) {
       try {
         // don't subscribe to type, key, stage combinations if they're already present
-        let subscription = getters.getSubscriptionsGroupedByType[stage][type].find(elem => {
-          if (type === 'random') {
+        let subscription = getters.getSubscriptionsGroupedByType[stage][type].find((elem: Subscription) => {
+          if (type === Subscription.QueryTypeEnum.Random) {
             return true
           }
           return elem.queryKey === key
@@ -189,11 +202,13 @@ const subscriptions = {
       }
     },
     async cleanAssignmentsFromSubscriptions ({commit, state, dispatch}, excludeActive = true) {
-      Object.values(state.subscriptions).forEach((subscription: any) => {
+      Object.values(state.subscriptions).forEach(subscription => {
         if (!excludeActive || subscription.pk !== state.activeSubscriptionPk) {
-          subscription.assignments.forEach(assignment => {
-            api.deleteAssignment({assignment})
-          })
+          if (subscription.assignments) {
+            subscription.assignments.forEach(assignment => {
+              api.deleteAssignment({assignment})
+            })
+          }
         }
       })
     },
@@ -206,7 +221,7 @@ const subscriptions = {
             // which will result get incorrectly interpreted as a an ended subscription
             return dispatch('getAssignmentsForActiveSubscription', 1)
           }).then(([promise]) => {
-            promise.then(assignment => {
+            promise.then((assignment: Assignment) => {
               commit(subscriptionMuts.ADD_ASSIGNMENT_TO_QUEUE, assignment)
               commit(subscriptionMuts.POP_ASSIGNMENT_FROM_QUEUE)
             })
@@ -243,17 +258,18 @@ const subscriptions = {
       commit(subscriptionMuts.SET_ASSIGNMENT_QUEUE, [])
       commit(subscriptionMuts.SET_ACTIVE_SUBSCRIPTION_PK, '')
     },
+    // TODO use enums here
     async subscribeToType ({commit, state, dispatch, getters}, type: Subscription.QueryTypeEnum) {
       switch (type) {
         case Subscription.QueryTypeEnum.Random:
-          return getters.availableStages.map(stage => {
+          return getters.availableStages.map((stage: string) => {
             dispatch('subscribeTo', {type, stage})
           })
         case Subscription.QueryTypeEnum.Exam:
           if (getters.isReviewer) {
             const stageKeyCartesian = cartesian(
               getters.availableStages, getters.availableExamTypeQueryKeys)
-            return stageKeyCartesian.map(([stage, key]) => {
+            return stageKeyCartesian.map(([stage, key]: [string, string]) => {
               dispatch('subscribeTo', {stage, type, key})
             })
           }
@@ -261,13 +277,14 @@ const subscriptions = {
         case Subscription.QueryTypeEnum.SubmissionType:
           const stageKeyCartesian = cartesian(
             getters.availableStages, getters.availableSubmissionTypeQueryKeys)
-          return stageKeyCartesian.map(([stage, key]) => {
+          return stageKeyCartesian.map(([stage, key]: [string, string]) => {
             dispatch('subscribeTo', {stage, type, key})
           })
       }
     },
-    subscribeToAll: once(async ({commit, state, dispatch, getters}) => {
-      return Promise.all(flatten(getters.availableTypes.map(type => {
+    subscribeToAll: once(async ({commit, state, dispatch, getters}:
+    ActionContext<SubscriptionsState, RootState>) => {
+      return Promise.all(flatten(getters.availableTypes.map((type: string) => {
         return dispatch('subscribeToType', type)
       })))
     })
diff --git a/frontend/src/store/modules/ui.ts b/frontend/src/store/modules/ui.ts
index 2cecc601..b1760cea 100644
--- a/frontend/src/store/modules/ui.ts
+++ b/frontend/src/store/modules/ui.ts
@@ -1,5 +1,14 @@
+import {Module} from 'vuex'
+import {RootState} from '@/store/store'
 
-function initialState () {
+interface UiState {
+  sideBarCollapsed: boolean
+  darkMode: boolean
+  darkModeUnlocked: boolean
+  showJumbotron: boolean
+}
+
+function initialState (): UiState {
   return {
     sideBarCollapsed: false,
     darkMode: false,
@@ -15,19 +24,19 @@ export const uiMut = Object.freeze({
   SET_SHOW_JUMBOTRON: 'SET_SHOW_JUMBOTRON'
 })
 
-const ui = {
+const ui: Module<UiState, RootState> = {
   state: initialState(),
   mutations: {
-    [uiMut.SET_SIDEBAR_COLLAPSED] (state, collapsed) {
+    [uiMut.SET_SIDEBAR_COLLAPSED] (state, collapsed: boolean) {
       state.sideBarCollapsed = collapsed
     },
-    [uiMut.SET_DARK_MODE] (state, val) {
+    [uiMut.SET_DARK_MODE] (state, val: boolean) {
       state.darkMode = val
     },
-    [uiMut.SET_DARK_MODE_UNLOCKED] (state, val) {
+    [uiMut.SET_DARK_MODE_UNLOCKED] (state, val: boolean) {
       state.darkModeUnlocked = val
     },
-    [uiMut.SET_SHOW_JUMBOTRON] (state, val) {
+    [uiMut.SET_SHOW_JUMBOTRON] (state, val: boolean) {
       state.showJumbotron = val
     }
   }
diff --git a/frontend/src/store/mutations.ts b/frontend/src/store/mutations.ts
index 96fff066..8c6f165e 100644
--- a/frontend/src/store/mutations.ts
+++ b/frontend/src/store/mutations.ts
@@ -1,6 +1,8 @@
 import Vue from 'vue'
 
-import {initialState} from '@/store/store'
+import {initialState, RootState} from '@/store/store'
+import {MutationTree} from 'vuex'
+import {Exam, Statistics, StudentInfoForListView, SubmissionNoType, SubmissionType, Tutor} from '@/models'
 
 export const mut = Object.freeze({
   SET_LAST_INTERACTION: 'SET_LAST_INTERACTION',
@@ -19,20 +21,20 @@ export const mut = Object.freeze({
   RESET_STATE: 'RESET_STATE'
 })
 
-const mutations = {
-  [mut.SET_EXAM_TYPES] (state, examTypes) {
-    state.examTypes = examTypes.reduce((acc, curr) => {
+const mutations: MutationTree<RootState> = {
+  [mut.SET_EXAM_TYPES] (state, examTypes: Array<Exam>) {
+    state.examTypes = examTypes.reduce((acc: {[pk: string]: Exam}, curr) => {
       acc[curr.pk] = curr
       return acc
     }, {})
   },
-  [mut.SET_STUDENTS] (state, students) {
-    state.students = students.reduce((acc, curr) => {
+  [mut.SET_STUDENTS] (state, students: Array<StudentInfoForListView>) {
+    state.students = students.reduce((acc: {[pk: string]: StudentInfoForListView}, curr) => {
       acc[curr.pk] = mapStudent(curr, state.studentMap)
       return acc
     }, {})
   },
-  [mut.SET_STUDENT] (state, student) {
+  [mut.SET_STUDENT] (state, student: StudentInfoForListView) {
     Vue.set(state.students, student.pk, mapStudent({
       ...state.students[student.pk],
       ...student
@@ -41,49 +43,25 @@ const mutations = {
   [mut.SET_STUDENT_MAP] (state, map) {
     state.studentMap = map
   },
-  [mut.SET_TUTORS] (state, tutors) {
+  [mut.SET_TUTORS] (state, tutors: Array<Tutor>) {
     state.tutors = tutors
   },
-  [mut.SET_SUBMISSION] (state, submission) {
+  [mut.SET_SUBMISSION] (state, submission: SubmissionNoType) {
     Vue.set(state.submissions, submission.pk, submission)
   },
-  [mut.SET_ALL_FEEDBACK] (state, feedbackList) {
-    state.feedback = feedbackList.reduce((acc, curr) => {
-      acc[curr.pk] = curr
-      return acc
-    }, {})
-  },
-  [mut.SET_FEEDBACK] (state, feedback) {
-    Vue.set(state.feedback, feedback.pk, {
-      ...state.feedback[feedback.pk],
-      ...feedback,
-      ofSubmissionType: state.submissionTypes[feedback['ofSubmissionType']]
-    })
-  },
-  [mut.SET_STATISTICS] (state, statistics) {
+  [mut.SET_STATISTICS] (state, statistics: Statistics) {
     state.statistics = {
       ...state.statistics,
       ...statistics
     }
   },
-  [mut.MAP_FEEDBACK_OF_SUBMISSION_TYPE] (state) {
-    Object.values(state.feedback).forEach(feedback => {
-      const submissionType = state.submissionTypes[feedback['ofSubmissionType']]
-      if (submissionType) {
-        feedback['ofSubmissionType'] = submissionType
-      }
-    })
-  },
-  [mut.UPDATE_SUBMISSION_TYPE] (state, submissionType) {
+  [mut.UPDATE_SUBMISSION_TYPE] (state, submissionType: SubmissionType) {
     const updatedSubmissionType = {
       ...state.submissionTypes[submissionType.pk],
       ...submissionType
     }
     Vue.set(state.submissionTypes, submissionType.pk, updatedSubmissionType)
   },
-  [mut.UPDATE_SUBMISSION] (state, {submissionPk, payload, key}) {
-    Vue.set(state.submissions[submissionPk], key, payload)
-  },
   [mut.SET_LAST_INTERACTION] (state) {
     state.lastAppInteraction = Date.now()
   },
@@ -92,8 +70,11 @@ const mutations = {
   }
 }
 
-function mapStudent (student, map) {
+function mapStudent (student: StudentInfoForListView, map: any) {
   if (Object.keys(map).length > 0) {
+    if (!student.matrikelNo) {
+      throw Error('Student objects need matrikelNo key in order to apply mapping')
+    }
     return {
       ...student,
       ...map[student.matrikelNo]
diff --git a/frontend/src/store/plugins/lastInteractionPlugin.ts b/frontend/src/store/plugins/lastInteractionPlugin.ts
index 7d0659e8..7c64f2ba 100644
--- a/frontend/src/store/plugins/lastInteractionPlugin.ts
+++ b/frontend/src/store/plugins/lastInteractionPlugin.ts
@@ -1,7 +1,8 @@
 import {mut} from '@/store/mutations'
+import {MutationPayload, Store} from 'vuex'
 
-export function lastInteraction (store) {
-  store.subscribe((mutation) => {
+export function lastInteraction (store: Store<void>) {
+  store.subscribe((mutation: MutationPayload) => {
     if (mutation.type !== mut.SET_LAST_INTERACTION) {
       store.commit(mut.SET_LAST_INTERACTION)
     }
diff --git a/frontend/src/store/store.ts b/frontend/src/store/store.ts
index db8c0a28..6ab9578e 100644
--- a/frontend/src/store/store.ts
+++ b/frontend/src/store/store.ts
@@ -14,20 +14,39 @@ import actions from './actions'
 import getters from './getters'
 import mutations from '@/store/mutations'
 import {lastInteraction} from '@/store/plugins/lastInteractionPlugin'
+import {
+  Exam,
+  Statistics,
+  StudentInfoForListView,
+  SubmissionNoType,
+  SubmissionType, Tutor
+} from '@/models'
 
 Vue.use(Vuex)
 
+export interface RootState {
+    lastAppInteraction: number
+    examTypes: {[pk: string]: Exam}
+    submissionTypes: {[pk: string]: SubmissionType}
+    submissions: {[pk: string]: SubmissionNoType}
+    students: {[pk: string]: StudentInfoForListView}
+    studentMap: {} // is used to map obfuscated student data back to the original
+    statistics: Statistics
+    tutors: Array<Tutor>
+}
 
-export function initialState () {
+export function initialState (): RootState {
   return {
     lastAppInteraction: Date.now(),
     examTypes: {},
     submissionTypes: {},
     submissions: {},
-    feedback: {},
     students: {},
     studentMap: {}, // is used to map obfuscated student data back to the original
     statistics: {
+      submissionsPerType: 0,
+      submissionsPerStudent: 0,
+      currentMeanScore: 0,
       submissionTypeProgress: []
     },
     tutors: []
diff --git a/frontend/src/util/helpers.ts b/frontend/src/util/helpers.ts
index b1d6ff27..11f1d8fe 100644
--- a/frontend/src/util/helpers.ts
+++ b/frontend/src/util/helpers.ts
@@ -1,3 +1,5 @@
+import {AxiosError} from "axios";
+import {Dispatch} from "vuex";
 
 export function nameSpacer (namespace: string) {
   return function (commitType: string) {
@@ -41,10 +43,10 @@ export function createComputedGetterSetter (
   {path: string, mutation: string, namespace:string}): GetSetPair {
   return {
     get (): any {
-      return getObjectValueByPath(this.$store.state, path)
+      return getObjectValueByPath((this as any).$store.state, path)
     },
     set (val: object): void {
-      this.$store.commit(`${namespace ? namespace + '/' : ''}${mutation}`, val)
+        (this as any).$store.commit(`${namespace ? namespace + '/' : ''}${mutation}`, val)
     }
   }
 }
@@ -80,9 +82,10 @@ export function mapStateToComputedGetterSetter (
 
 // thanks to rsp
 // https://stackoverflow.com/questions/12303989/cartesian-product-of-multiple-arrays-in-javascript/43053803#43053803
-let cartesianHelper = (a, b) => [].concat(...a.map(a => b.map(b => [].concat(a, b))))
-export function cartesian (a, b, ...c) {
-  // @ts-ignore can be ignored since cartesian is only recursively called if b si truthy
+function cartesianHelper (a: Array<any>, b: Array<any>): Array<Array<any>> {
+    return ([] as Array<any>).concat(...a.map((a: any) => b.map((b: any) => [].concat(a, b))))
+}
+export function cartesian (a: Array<any>, b?: Array<any>, ...c: Array<Array<any>>): Array<Array<any>> {
   return b ? cartesian(cartesianHelper(a, b), ...c) : a
 }
 
@@ -93,24 +96,33 @@ export function flatten (list: any[]): any[] {
   )
 }
 
-export function objectifyArray<T> (arr: T[], key = 'pk'): {[key: string]: T} {
-  return arr.reduce((acc, curr) => {
+export function objectifyArray<T, P extends keyof T> (arr: T[], key: P): {[index: string]: T} {
+  return arr.reduce((acc: any, curr) => {
     acc[curr[key]] = curr
     return acc
   }, {})
 }
 
-export function once (fn: Function, context?: object) {
-  let result
-  return function () {
+interface OnceFunc {
+    (): any
+    reset: () => void
+}
+
+export function once (fn: Function, context?: object): OnceFunc {
+  let result: any
+  let wrapped = <OnceFunc> function (this: any) {
     if (!result) {
       result = fn.apply(context || this, arguments)
     }
     return result
   }
+  wrapped.reset = function () {result = undefined}
+  return wrapped
 }
 
-export function handleError (err, dispatch, fallbackMsg) {
+export function handleError (err: Error, dispatch: Dispatch, fallbackMsg: string): any
+export function handleError (err: AxiosError, dispatch: Dispatch, fallbackMsg: string): any
+export function handleError (err: any, dispatch: Dispatch, fallbackMsg: string): any {
   if (err.response) {
     if (err.response.status === 401) {
       dispatch('logout', 'You\'ve been logged out')
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index 7959a1cf..264e7940 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -8,32 +8,6 @@
   dependencies:
     "@babel/highlight" "7.0.0-beta.44"
 
-"@babel/code-frame@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.47.tgz#d18c2f4c4ba8d093a2bcfab5616593bfe2441a27"
-  dependencies:
-    "@babel/highlight" "7.0.0-beta.47"
-
-"@babel/core@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.0.0-beta.47.tgz#b9c164fb9a1e1083f067c236a9da1d7a7d759271"
-  dependencies:
-    "@babel/code-frame" "7.0.0-beta.47"
-    "@babel/generator" "7.0.0-beta.47"
-    "@babel/helpers" "7.0.0-beta.47"
-    "@babel/template" "7.0.0-beta.47"
-    "@babel/traverse" "7.0.0-beta.47"
-    "@babel/types" "7.0.0-beta.47"
-    babylon "7.0.0-beta.47"
-    convert-source-map "^1.1.0"
-    debug "^3.1.0"
-    json5 "^0.5.0"
-    lodash "^4.17.5"
-    micromatch "^2.3.11"
-    resolve "^1.3.2"
-    semver "^5.4.1"
-    source-map "^0.5.0"
-
 "@babel/generator@7.0.0-beta.44":
   version "7.0.0-beta.44"
   resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.44.tgz#c7e67b9b5284afcf69b309b50d7d37f3e5033d42"
@@ -44,52 +18,6 @@
     source-map "^0.5.0"
     trim-right "^1.0.1"
 
-"@babel/generator@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.47.tgz#1835709f377cc4d2a4affee6d9258a10bbf3b9d1"
-  dependencies:
-    "@babel/types" "7.0.0-beta.47"
-    jsesc "^2.5.1"
-    lodash "^4.17.5"
-    source-map "^0.5.0"
-    trim-right "^1.0.1"
-
-"@babel/helper-annotate-as-pure@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0-beta.47.tgz#354fb596055d9db369211bf075f0d5e93904d6f6"
-  dependencies:
-    "@babel/types" "7.0.0-beta.47"
-
-"@babel/helper-builder-binary-assignment-operator-visitor@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.0.0-beta.47.tgz#d5917c29ee3d68abc2c72f604bc043f6e056e907"
-  dependencies:
-    "@babel/helper-explode-assignable-expression" "7.0.0-beta.47"
-    "@babel/types" "7.0.0-beta.47"
-
-"@babel/helper-call-delegate@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.0.0-beta.47.tgz#96b7804397075f722a4030d3876f51ec19d8829b"
-  dependencies:
-    "@babel/helper-hoist-variables" "7.0.0-beta.47"
-    "@babel/traverse" "7.0.0-beta.47"
-    "@babel/types" "7.0.0-beta.47"
-
-"@babel/helper-define-map@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.0.0-beta.47.tgz#43a9def87c5166dc29630d51b3da9cc4320c131c"
-  dependencies:
-    "@babel/helper-function-name" "7.0.0-beta.47"
-    "@babel/types" "7.0.0-beta.47"
-    lodash "^4.17.5"
-
-"@babel/helper-explode-assignable-expression@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.0.0-beta.47.tgz#56b688e282a698f4d1cf135453a11ae8af870a19"
-  dependencies:
-    "@babel/traverse" "7.0.0-beta.47"
-    "@babel/types" "7.0.0-beta.47"
-
 "@babel/helper-function-name@7.0.0-beta.44":
   version "7.0.0-beta.44"
   resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz#e18552aaae2231100a6e485e03854bc3532d44dd"
@@ -98,128 +26,18 @@
     "@babel/template" "7.0.0-beta.44"
     "@babel/types" "7.0.0-beta.44"
 
-"@babel/helper-function-name@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.47.tgz#8057d63e951e85c57c02cdfe55ad7608d73ffb7d"
-  dependencies:
-    "@babel/helper-get-function-arity" "7.0.0-beta.47"
-    "@babel/template" "7.0.0-beta.47"
-    "@babel/types" "7.0.0-beta.47"
-
 "@babel/helper-get-function-arity@7.0.0-beta.44":
   version "7.0.0-beta.44"
   resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz#d03ca6dd2b9f7b0b1e6b32c56c72836140db3a15"
   dependencies:
     "@babel/types" "7.0.0-beta.44"
 
-"@babel/helper-get-function-arity@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.47.tgz#2de04f97c14b094b55899d3fa83144a16d207510"
-  dependencies:
-    "@babel/types" "7.0.0-beta.47"
-
-"@babel/helper-hoist-variables@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.0.0-beta.47.tgz#ce295d1d723fe22b2820eaec748ed701aa5ae3d0"
-  dependencies:
-    "@babel/types" "7.0.0-beta.47"
-
-"@babel/helper-member-expression-to-functions@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0-beta.47.tgz#35bfcf1d16dce481ef3dec66d5a1ae6a7d80bb45"
-  dependencies:
-    "@babel/types" "7.0.0-beta.47"
-
-"@babel/helper-module-imports@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.47.tgz#5af072029ffcfbece6ffbaf5d9984c75580f3f04"
-  dependencies:
-    "@babel/types" "7.0.0-beta.47"
-    lodash "^4.17.5"
-
-"@babel/helper-module-transforms@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.0.0-beta.47.tgz#7eff91fc96873bd7b8d816698f1a69bbc01f3c38"
-  dependencies:
-    "@babel/helper-module-imports" "7.0.0-beta.47"
-    "@babel/helper-simple-access" "7.0.0-beta.47"
-    "@babel/helper-split-export-declaration" "7.0.0-beta.47"
-    "@babel/template" "7.0.0-beta.47"
-    "@babel/types" "7.0.0-beta.47"
-    lodash "^4.17.5"
-
-"@babel/helper-optimise-call-expression@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0-beta.47.tgz#085d864d0613c5813c1b7c71b61bea36f195929e"
-  dependencies:
-    "@babel/types" "7.0.0-beta.47"
-
-"@babel/helper-plugin-utils@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0-beta.47.tgz#4f564117ec39f96cf60fafcde35c9ddce0e008fd"
-
-"@babel/helper-regex@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.0.0-beta.47.tgz#b8e3b53132c4edbb04804242c02ffe4d60316971"
-  dependencies:
-    lodash "^4.17.5"
-
-"@babel/helper-remap-async-to-generator@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.0.0-beta.47.tgz#444dc362f61470bd61a745ebb364431d9ca186c2"
-  dependencies:
-    "@babel/helper-annotate-as-pure" "7.0.0-beta.47"
-    "@babel/helper-wrap-function" "7.0.0-beta.47"
-    "@babel/template" "7.0.0-beta.47"
-    "@babel/traverse" "7.0.0-beta.47"
-    "@babel/types" "7.0.0-beta.47"
-
-"@babel/helper-replace-supers@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.0.0-beta.47.tgz#310b206a302868a792b659455ceba27db686cbb7"
-  dependencies:
-    "@babel/helper-member-expression-to-functions" "7.0.0-beta.47"
-    "@babel/helper-optimise-call-expression" "7.0.0-beta.47"
-    "@babel/traverse" "7.0.0-beta.47"
-    "@babel/types" "7.0.0-beta.47"
-
-"@babel/helper-simple-access@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.0.0-beta.47.tgz#234d754acbda9251a10db697ef50181eab125042"
-  dependencies:
-    "@babel/template" "7.0.0-beta.47"
-    "@babel/types" "7.0.0-beta.47"
-    lodash "^4.17.5"
-
 "@babel/helper-split-export-declaration@7.0.0-beta.44":
   version "7.0.0-beta.44"
   resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz#c0b351735e0fbcb3822c8ad8db4e583b05ebd9dc"
   dependencies:
     "@babel/types" "7.0.0-beta.44"
 
-"@babel/helper-split-export-declaration@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.47.tgz#e11277855472d8d83baf22f2d0186c4a2059b09a"
-  dependencies:
-    "@babel/types" "7.0.0-beta.47"
-
-"@babel/helper-wrap-function@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.0.0-beta.47.tgz#6528b44a3ccb4f3aeeb79add0a88192f7eb81161"
-  dependencies:
-    "@babel/helper-function-name" "7.0.0-beta.47"
-    "@babel/template" "7.0.0-beta.47"
-    "@babel/traverse" "7.0.0-beta.47"
-    "@babel/types" "7.0.0-beta.47"
-
-"@babel/helpers@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.0.0-beta.47.tgz#f9b42ed2e4d5f75ec0fb2e792c173e451e8d40fd"
-  dependencies:
-    "@babel/template" "7.0.0-beta.47"
-    "@babel/traverse" "7.0.0-beta.47"
-    "@babel/types" "7.0.0-beta.47"
-
 "@babel/highlight@7.0.0-beta.44":
   version "7.0.0-beta.44"
   resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.44.tgz#18c94ce543916a80553edcdcf681890b200747d5"
@@ -228,348 +46,6 @@
     esutils "^2.0.2"
     js-tokens "^3.0.0"
 
-"@babel/highlight@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.47.tgz#8fbc83fb2a21f0bd2b95cdbeb238cf9689cad494"
-  dependencies:
-    chalk "^2.0.0"
-    esutils "^2.0.2"
-    js-tokens "^3.0.0"
-
-"@babel/plugin-proposal-async-generator-functions@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.0.0-beta.47.tgz#571142284708c5ad4ec904d9aa705461a010be53"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    "@babel/helper-remap-async-to-generator" "7.0.0-beta.47"
-    "@babel/plugin-syntax-async-generators" "7.0.0-beta.47"
-
-"@babel/plugin-proposal-class-properties@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.0.0-beta.47.tgz#08c1a1dfc92d0f5c37b39096c6fb883e1ca4b0f5"
-  dependencies:
-    "@babel/helper-function-name" "7.0.0-beta.47"
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    "@babel/helper-replace-supers" "7.0.0-beta.47"
-    "@babel/plugin-syntax-class-properties" "7.0.0-beta.47"
-
-"@babel/plugin-proposal-decorators@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.0.0-beta.47.tgz#5e8943c8f8eb3301f911ef0dcd3ed64cf28c723e"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    "@babel/plugin-syntax-decorators" "7.0.0-beta.47"
-
-"@babel/plugin-proposal-object-rest-spread@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.0.0-beta.47.tgz#e1529fddc88e948868ee1d0edaa27ebd9502322d"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    "@babel/plugin-syntax-object-rest-spread" "7.0.0-beta.47"
-
-"@babel/plugin-proposal-optional-catch-binding@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.0.0-beta.47.tgz#8c6453919537517ea773bb8f3fceda4250795efa"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    "@babel/plugin-syntax-optional-catch-binding" "7.0.0-beta.47"
-
-"@babel/plugin-proposal-unicode-property-regex@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.0.0-beta.47.tgz#34d7e4811bdc4f512400bb29d01051842528c8d5"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    "@babel/helper-regex" "7.0.0-beta.47"
-    regexpu-core "^4.1.4"
-
-"@babel/plugin-syntax-async-generators@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.0.0-beta.47.tgz#8ab94852bf348badc866af85bd852221f0961256"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-syntax-class-properties@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.0.0-beta.47.tgz#de52bed12fd472c848e1562f57dd4a202fe27f11"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-syntax-decorators@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.0.0-beta.47.tgz#a42f10fcd651940bc475d93b3ac23432b4a8a293"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-syntax-dynamic-import@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.0.0-beta.47.tgz#ee964915014a687701ee8e15c289e31a7c899e60"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-syntax-jsx@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.0.0-beta.47.tgz#f3849d94288695d724bd205b4f6c3c99e4ec24a4"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-syntax-object-rest-spread@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.0.0-beta.47.tgz#21da514d94c138b2261ca09f0dec9abadce16185"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-syntax-optional-catch-binding@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.0.0-beta.47.tgz#0b1c52b066aa36893c41450773a5adb904cd4024"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-arrow-functions@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.0.0-beta.47.tgz#d6eecda4c652b909e3088f0983ebaf8ec292984b"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-async-to-generator@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.0.0-beta.47.tgz#5723816ea1e91fa313a84e6ee9cc12ff31d46610"
-  dependencies:
-    "@babel/helper-module-imports" "7.0.0-beta.47"
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    "@babel/helper-remap-async-to-generator" "7.0.0-beta.47"
-
-"@babel/plugin-transform-block-scoped-functions@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.0.0-beta.47.tgz#e422278e06c797b43c45f459d83c7af9d6237002"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-block-scoping@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.0.0-beta.47.tgz#b737cc58a81bea57efd5bda0baef9a43a25859ad"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    lodash "^4.17.5"
-
-"@babel/plugin-transform-classes@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.0.0-beta.47.tgz#7aff9cbe7b26fd94d7a9f97fa90135ef20c93fb6"
-  dependencies:
-    "@babel/helper-annotate-as-pure" "7.0.0-beta.47"
-    "@babel/helper-define-map" "7.0.0-beta.47"
-    "@babel/helper-function-name" "7.0.0-beta.47"
-    "@babel/helper-optimise-call-expression" "7.0.0-beta.47"
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    "@babel/helper-replace-supers" "7.0.0-beta.47"
-    "@babel/helper-split-export-declaration" "7.0.0-beta.47"
-    globals "^11.1.0"
-
-"@babel/plugin-transform-computed-properties@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.0.0-beta.47.tgz#56ef2a021769a2b65e90a3e12fd10b791da9f3e0"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-destructuring@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.0.0-beta.47.tgz#452b607775fd1c4d10621997837189efc0a6d428"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-dotall-regex@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.0.0-beta.47.tgz#d8da9b706d4bfc68dec9d565661f83e6e8036636"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    "@babel/helper-regex" "7.0.0-beta.47"
-    regexpu-core "^4.1.3"
-
-"@babel/plugin-transform-duplicate-keys@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.0.0-beta.47.tgz#4aabeda051ca3007e33a207db08f1a0cf9bd253b"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-exponentiation-operator@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.0.0-beta.47.tgz#930e1abf5db9f4db5b63dbf97f3581ad0be1e907"
-  dependencies:
-    "@babel/helper-builder-binary-assignment-operator-visitor" "7.0.0-beta.47"
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-for-of@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.0.0-beta.47.tgz#527d5dc24e4a4ad0fc1d0a3990d29968cb984e76"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-function-name@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.0.0-beta.47.tgz#fb443c81cc77f3206a863b730b35c8c553ce5041"
-  dependencies:
-    "@babel/helper-function-name" "7.0.0-beta.47"
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-literals@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.0.0-beta.47.tgz#448fad196f062163684a38f10f14e83315892e9c"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-modules-amd@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.0.0-beta.47.tgz#84564419b11c1be6b9fcd4c7b3a6737f2335aac4"
-  dependencies:
-    "@babel/helper-module-transforms" "7.0.0-beta.47"
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-modules-commonjs@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.0.0-beta.47.tgz#dfe5c6d867aa9614e55f7616736073edb3aab887"
-  dependencies:
-    "@babel/helper-module-transforms" "7.0.0-beta.47"
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    "@babel/helper-simple-access" "7.0.0-beta.47"
-
-"@babel/plugin-transform-modules-systemjs@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.0.0-beta.47.tgz#8514dbcdfca3345abd690059e7e8544e16ecbf05"
-  dependencies:
-    "@babel/helper-hoist-variables" "7.0.0-beta.47"
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-modules-umd@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.0.0-beta.47.tgz#6dcfb9661fdd131b20b721044746a7a309882918"
-  dependencies:
-    "@babel/helper-module-transforms" "7.0.0-beta.47"
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-new-target@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.0.0-beta.47.tgz#4b5cb7ce30d7bffa105a1f43ed07d6ae206a4155"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-object-super@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.0.0-beta.47.tgz#ca8e5f326c5011c879f3a6ed749e58bd10fff05d"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    "@babel/helper-replace-supers" "7.0.0-beta.47"
-
-"@babel/plugin-transform-parameters@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.0.0-beta.47.tgz#46a4236040a6552a5f165fb3ddd60368954b0ddd"
-  dependencies:
-    "@babel/helper-call-delegate" "7.0.0-beta.47"
-    "@babel/helper-get-function-arity" "7.0.0-beta.47"
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-regenerator@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.0.0-beta.47.tgz#86500e1c404055fb98fc82b73b09bd053cacb516"
-  dependencies:
-    regenerator-transform "^0.12.3"
-
-"@babel/plugin-transform-runtime@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.0.0-beta.47.tgz#1700938fa8710909cbf28f7dd39f9b40688b09fd"
-  dependencies:
-    "@babel/helper-module-imports" "7.0.0-beta.47"
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-shorthand-properties@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.0.0-beta.47.tgz#00be44c4fad8fe2c00ed18ea15ea3c88dd519dbb"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-spread@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.0.0-beta.47.tgz#3feadb02292ed1e9b75090d651b9df88a7ab5c50"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-sticky-regex@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.0.0-beta.47.tgz#c0aa347d76b5dc87d3b37ac016ada3f950605131"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    "@babel/helper-regex" "7.0.0-beta.47"
-
-"@babel/plugin-transform-template-literals@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.0.0-beta.47.tgz#5f7b5badf64c4c5da79026aeab03001e62a6ee5f"
-  dependencies:
-    "@babel/helper-annotate-as-pure" "7.0.0-beta.47"
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-typeof-symbol@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.0.0-beta.47.tgz#03c612ec09213eb386a81d5fa67c234ee4b2034c"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-
-"@babel/plugin-transform-unicode-regex@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.0.0-beta.47.tgz#efed0b2f1dfbf28283502234a95b4be88f7fdcb6"
-  dependencies:
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    "@babel/helper-regex" "7.0.0-beta.47"
-    regexpu-core "^4.1.3"
-
-"@babel/preset-env@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.0.0-beta.47.tgz#a3dab3b5fac4de56e3510bdbcb528f1cbdedbe2d"
-  dependencies:
-    "@babel/helper-module-imports" "7.0.0-beta.47"
-    "@babel/helper-plugin-utils" "7.0.0-beta.47"
-    "@babel/plugin-proposal-async-generator-functions" "7.0.0-beta.47"
-    "@babel/plugin-proposal-object-rest-spread" "7.0.0-beta.47"
-    "@babel/plugin-proposal-optional-catch-binding" "7.0.0-beta.47"
-    "@babel/plugin-proposal-unicode-property-regex" "7.0.0-beta.47"
-    "@babel/plugin-syntax-async-generators" "7.0.0-beta.47"
-    "@babel/plugin-syntax-object-rest-spread" "7.0.0-beta.47"
-    "@babel/plugin-syntax-optional-catch-binding" "7.0.0-beta.47"
-    "@babel/plugin-transform-arrow-functions" "7.0.0-beta.47"
-    "@babel/plugin-transform-async-to-generator" "7.0.0-beta.47"
-    "@babel/plugin-transform-block-scoped-functions" "7.0.0-beta.47"
-    "@babel/plugin-transform-block-scoping" "7.0.0-beta.47"
-    "@babel/plugin-transform-classes" "7.0.0-beta.47"
-    "@babel/plugin-transform-computed-properties" "7.0.0-beta.47"
-    "@babel/plugin-transform-destructuring" "7.0.0-beta.47"
-    "@babel/plugin-transform-dotall-regex" "7.0.0-beta.47"
-    "@babel/plugin-transform-duplicate-keys" "7.0.0-beta.47"
-    "@babel/plugin-transform-exponentiation-operator" "7.0.0-beta.47"
-    "@babel/plugin-transform-for-of" "7.0.0-beta.47"
-    "@babel/plugin-transform-function-name" "7.0.0-beta.47"
-    "@babel/plugin-transform-literals" "7.0.0-beta.47"
-    "@babel/plugin-transform-modules-amd" "7.0.0-beta.47"
-    "@babel/plugin-transform-modules-commonjs" "7.0.0-beta.47"
-    "@babel/plugin-transform-modules-systemjs" "7.0.0-beta.47"
-    "@babel/plugin-transform-modules-umd" "7.0.0-beta.47"
-    "@babel/plugin-transform-new-target" "7.0.0-beta.47"
-    "@babel/plugin-transform-object-super" "7.0.0-beta.47"
-    "@babel/plugin-transform-parameters" "7.0.0-beta.47"
-    "@babel/plugin-transform-regenerator" "7.0.0-beta.47"
-    "@babel/plugin-transform-shorthand-properties" "7.0.0-beta.47"
-    "@babel/plugin-transform-spread" "7.0.0-beta.47"
-    "@babel/plugin-transform-sticky-regex" "7.0.0-beta.47"
-    "@babel/plugin-transform-template-literals" "7.0.0-beta.47"
-    "@babel/plugin-transform-typeof-symbol" "7.0.0-beta.47"
-    "@babel/plugin-transform-unicode-regex" "7.0.0-beta.47"
-    browserslist "^3.0.0"
-    invariant "^2.2.2"
-    semver "^5.3.0"
-
-"@babel/runtime@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.47.tgz#273f5e71629e80f6cbcd7507503848615e59f7e0"
-  dependencies:
-    core-js "^2.5.3"
-    regenerator-runtime "^0.11.1"
-
 "@babel/template@7.0.0-beta.44":
   version "7.0.0-beta.44"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f"
@@ -579,15 +55,6 @@
     babylon "7.0.0-beta.44"
     lodash "^4.2.0"
 
-"@babel/template@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.47.tgz#0473970a7c0bee7a1a18c1ca999d3ba5e5bad83d"
-  dependencies:
-    "@babel/code-frame" "7.0.0-beta.47"
-    "@babel/types" "7.0.0-beta.47"
-    babylon "7.0.0-beta.47"
-    lodash "^4.17.5"
-
 "@babel/traverse@7.0.0-beta.44":
   version "7.0.0-beta.44"
   resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.44.tgz#a970a2c45477ad18017e2e465a0606feee0d2966"
@@ -603,21 +70,6 @@
     invariant "^2.2.0"
     lodash "^4.2.0"
 
-"@babel/traverse@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.47.tgz#0e57fdbb9ff3a909188b6ebf1e529c641e6c82a4"
-  dependencies:
-    "@babel/code-frame" "7.0.0-beta.47"
-    "@babel/generator" "7.0.0-beta.47"
-    "@babel/helper-function-name" "7.0.0-beta.47"
-    "@babel/helper-split-export-declaration" "7.0.0-beta.47"
-    "@babel/types" "7.0.0-beta.47"
-    babylon "7.0.0-beta.47"
-    debug "^3.1.0"
-    globals "^11.1.0"
-    invariant "^2.2.0"
-    lodash "^4.17.5"
-
 "@babel/types@7.0.0-beta.44":
   version "7.0.0-beta.44"
   resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.44.tgz#6b1b164591f77dec0a0342aca995f2d046b3a757"
@@ -626,14 +78,6 @@
     lodash "^4.2.0"
     to-fast-properties "^2.0.0"
 
-"@babel/types@7.0.0-beta.47":
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.47.tgz#e6fcc1a691459002c2671d558a586706dddaeef8"
-  dependencies:
-    esutils "^2.0.2"
-    lodash "^4.17.5"
-    to-fast-properties "^2.0.0"
-
 "@intervolga/optimize-cssnano-plugin@^1.0.5":
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/@intervolga/optimize-cssnano-plugin/-/optimize-cssnano-plugin-1.0.6.tgz#be7c7846128b88f6a9b1d1261a0ad06eb5c0fdf8"
@@ -657,6 +101,10 @@
   version "4.1.4"
   resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.4.tgz#5ca073b330d90b4066d6ce18f60d57f2084ce8ca"
 
+"@types/highlight.js@^9.12.3":
+  version "9.12.3"
+  resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.3.tgz#b672cfaac25cbbc634a0fd92c515f66faa18dbca"
+
 "@types/mocha@^5.2.4":
   version "5.2.5"
   resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.5.tgz#8a4accfc403c124a0bafe8a9fc61a05ec1032073"
@@ -665,33 +113,10 @@
   version "10.5.5"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.5.tgz#8e84d24e896cd77b0d4f73df274027e3149ec2ba"
 
-"@vue/babel-preset-app@^3.0.0-rc.10":
-  version "3.0.0-rc.10"
-  resolved "https://registry.yarnpkg.com/@vue/babel-preset-app/-/babel-preset-app-3.0.0-rc.10.tgz#da46d88860011dca60954c0c8692bdc68be552a6"
-  dependencies:
-    "@babel/plugin-proposal-class-properties" "7.0.0-beta.47"
-    "@babel/plugin-proposal-decorators" "7.0.0-beta.47"
-    "@babel/plugin-syntax-dynamic-import" "7.0.0-beta.47"
-    "@babel/plugin-syntax-jsx" "7.0.0-beta.47"
-    "@babel/plugin-transform-runtime" "7.0.0-beta.47"
-    "@babel/preset-env" "7.0.0-beta.47"
-    "@babel/runtime" "7.0.0-beta.47"
-    babel-helper-vue-jsx-merge-props "^2.0.3"
-    babel-plugin-dynamic-import-node "^2.0.0"
-    babel-plugin-transform-vue-jsx "^4.0.1"
-
 "@vue/cli-overlay@^3.0.0-rc.10":
   version "3.0.0-rc.10"
   resolved "https://registry.yarnpkg.com/@vue/cli-overlay/-/cli-overlay-3.0.0-rc.10.tgz#d7fc86e1b1afc1b7ae52875c388a5532c0c10505"
 
-"@vue/cli-plugin-babel@^3.0.0-rc.10":
-  version "3.0.0-rc.10"
-  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-babel/-/cli-plugin-babel-3.0.0-rc.10.tgz#616ac0958927106353d37e61def3cf593c5e4a6f"
-  dependencies:
-    "@babel/core" "7.0.0-beta.47"
-    "@vue/babel-preset-app" "^3.0.0-rc.10"
-    babel-loader "^8.0.0-0"
-
 "@vue/cli-plugin-e2e-nightwatch@^3.0.0-rc.10":
   version "3.0.0-rc.10"
   resolved "https://registry.yarnpkg.com/@vue/cli-plugin-e2e-nightwatch/-/cli-plugin-e2e-nightwatch-3.0.0-rc.10.tgz#40597ba93fff523f142eeb284bc30be108c78dcf"
@@ -1328,36 +753,6 @@ babel-eslint@^8.2.5:
     eslint-scope "3.7.1"
     eslint-visitor-keys "^1.0.0"
 
-babel-helper-vue-jsx-merge-props@^2.0.3:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz#22aebd3b33902328e513293a8e4992b384f9f1b6"
-
-babel-loader@^8.0.0-0:
-  version "8.0.0-beta.4"
-  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.0-beta.4.tgz#c3fab00696c385c70c04dbe486391f0eb996f345"
-  dependencies:
-    find-cache-dir "^1.0.0"
-    loader-utils "^1.0.2"
-    mkdirp "^0.5.1"
-    util.promisify "^1.0.0"
-
-babel-plugin-dynamic-import-node@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.0.0.tgz#d6fc3f6c5e3bdc34e49c15faca7ce069755c0a57"
-  dependencies:
-    babel-plugin-syntax-dynamic-import "^6.18.0"
-    object.assign "^4.1.0"
-
-babel-plugin-syntax-dynamic-import@^6.18.0:
-  version "6.18.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da"
-
-babel-plugin-transform-vue-jsx@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-4.0.1.tgz#2c8bddce87a6ef09eaa59869ff1bfbeeafc5f88d"
-  dependencies:
-    esutils "^2.0.2"
-
 babel-runtime@^6.18.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
@@ -1369,10 +764,6 @@ babylon@7.0.0-beta.44:
   version "7.0.0-beta.44"
   resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.44.tgz#89159e15e6e30c5096e22d738d8c0af8a0e8ca1d"
 
-babylon@7.0.0-beta.47:
-  version "7.0.0-beta.47"
-  resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.47.tgz#6d1fa44f0abec41ab7c780481e62fd9aafbdea80"
-
 balanced-match@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
@@ -1556,7 +947,7 @@ browserify-zlib@^0.2.0:
   dependencies:
     pako "~1.0.5"
 
-browserslist@^3.0.0, browserslist@^3.2.8:
+browserslist@^3.2.8:
   version "3.2.8"
   resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6"
   dependencies:
@@ -2022,10 +1413,6 @@ content-type@~1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
 
-convert-source-map@^1.1.0:
-  version "1.5.1"
-  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
-
 cookie-signature@1.0.6:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
@@ -2062,7 +1449,7 @@ copy-webpack-plugin@^4.5.2:
     p-limit "^1.0.0"
     serialize-javascript "^1.4.0"
 
-core-js@^2.4.0, core-js@^2.5.3:
+core-js@^2.4.0:
   version "2.5.7"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
 
@@ -3965,7 +3352,7 @@ interpret@^1.0.1:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
 
-invariant@^2.2.0, invariant@^2.2.2:
+invariant@^2.2.0:
   version "2.2.4"
   resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
   dependencies:
@@ -4841,7 +4228,7 @@ methods@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
 
-micromatch@^2.1.5, micromatch@^2.3.11:
+micromatch@^2.1.5:
   version "2.3.11"
   resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
   dependencies:
@@ -6018,10 +5405,6 @@ pretty-error@^2.0.2:
     renderkid "^2.0.1"
     utila "~0.4"
 
-private@^0.1.6:
-  version "0.1.8"
-  resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
-
 process-nextick-args@~2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
@@ -6278,26 +5661,14 @@ reduce-css-calc@^2.0.0:
     css-unit-converter "^1.1.1"
     postcss-value-parser "^3.3.0"
 
-regenerate-unicode-properties@^7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz#107405afcc4a190ec5ed450ecaa00ed0cafa7a4c"
-  dependencies:
-    regenerate "^1.4.0"
-
-regenerate@^1.2.1, regenerate@^1.4.0:
+regenerate@^1.2.1:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
 
-regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1:
+regenerator-runtime@^0.11.0:
   version "0.11.1"
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
 
-regenerator-transform@^0.12.3:
-  version "0.12.4"
-  resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.12.4.tgz#aa9b6c59f4b97be080e972506c560b3bccbfcff0"
-  dependencies:
-    private "^0.1.6"
-
 regex-cache@^0.4.2:
   version "0.4.4"
   resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd"
@@ -6323,37 +5694,16 @@ regexpu-core@^1.0.0:
     regjsgen "^0.2.0"
     regjsparser "^0.1.4"
 
-regexpu-core@^4.1.3, regexpu-core@^4.1.4:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.2.0.tgz#a3744fa03806cffe146dea4421a3e73bdcc47b1d"
-  dependencies:
-    regenerate "^1.4.0"
-    regenerate-unicode-properties "^7.0.0"
-    regjsgen "^0.4.0"
-    regjsparser "^0.3.0"
-    unicode-match-property-ecmascript "^1.0.4"
-    unicode-match-property-value-ecmascript "^1.0.2"
-
 regjsgen@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
 
-regjsgen@^0.4.0:
-  version "0.4.0"
-  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.4.0.tgz#c1eb4c89a209263f8717c782591523913ede2561"
-
 regjsparser@^0.1.4:
   version "0.1.5"
   resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"
   dependencies:
     jsesc "~0.5.0"
 
-regjsparser@^0.3.0:
-  version "0.3.0"
-  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.3.0.tgz#3c326da7fcfd69fa0d332575a41c8c0cdf588c96"
-  dependencies:
-    jsesc "~0.5.0"
-
 relateurl@0.2.x:
   version "0.2.7"
   resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
@@ -7331,25 +6681,6 @@ uglifyjs-webpack-plugin@^1.2.4, uglifyjs-webpack-plugin@^1.2.7:
     webpack-sources "^1.1.0"
     worker-farm "^1.5.2"
 
-unicode-canonical-property-names-ecmascript@^1.0.4:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
-
-unicode-match-property-ecmascript@^1.0.4:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c"
-  dependencies:
-    unicode-canonical-property-names-ecmascript "^1.0.4"
-    unicode-property-aliases-ecmascript "^1.0.4"
-
-unicode-match-property-value-ecmascript@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz#9f1dc76926d6ccf452310564fd834ace059663d4"
-
-unicode-property-aliases-ecmascript@^1.0.4:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz#5a533f31b4317ea76f17d807fa0d116546111dd0"
-
 union-value@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"
@@ -7450,7 +6781,7 @@ util-deprecate@~1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
 
-util.promisify@1.0.0, util.promisify@^1.0.0, util.promisify@~1.0.0:
+util.promisify@1.0.0, util.promisify@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030"
   dependencies:
diff --git a/grady/settings/default.py b/grady/settings/default.py
index a9a9fbee..24cbddfa 100644
--- a/grady/settings/default.py
+++ b/grady/settings/default.py
@@ -147,9 +147,12 @@ REST_FRAMEWORK = {
     'DEFAULT_PARSER_CLASSES': (
         'djangorestframework_camel_case.parser.CamelCaseJSONParser',
     ),
-    # 'JSON_UNDERSCOREIZE': {
-    #     'no_underscore_before_number': True,
-    # },
+}
+
+JSON_CAMEL_CASE = {
+    'JSON_UNDERSCOREIZE': {
+        'no_underscore_before_number': True,
+    },
 }
 
 JWT_AUTH = {
diff --git a/requirements.txt b/requirements.txt
index 5389e454..3a910671 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,8 +3,10 @@ django-extensions~=2.1
 djangorestframework-csv~=2.0.0
 djangorestframework-jwt~=1.11.0
 djangorestframework~=3.8
+git+https://github.com/robinhundt/djangorestframework-camel-case
 Django~=2.1
 drf-dynamic-fields~=0.3.0
+drf-yasg
 gevent~=1.3.2
 gunicorn~=19.7.0
 psycopg2-binary~=2.7.4
-- 
GitLab


From 0128df94a2f12e07f60e15b166dd9a71614b4fb9 Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Tue, 14 Aug 2018 10:18:41 +0200
Subject: [PATCH 5/9] Added mips and haskell options in SubmissionType Prog.
 lang

---
 core/models.py   |  4 ++++
 util/importer.py | 28 ++++++++++++++++++++--------
 2 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/core/models.py b/core/models.py
index f5e6e2bf..bc33deec 100644
--- a/core/models.py
+++ b/core/models.py
@@ -109,10 +109,14 @@ class SubmissionType(models.Model):
 
     C = 'c'
     JAVA = 'java'
+    MIPS = 'mipsasm'
+    HASKELL = 'haskell'
 
     LANGUAGE_CHOICES = (
         (C, 'C syntax highlighting'),
         (JAVA, 'Java syntax highlighting'),
+        (MIPS, 'Mips syntax highlighting'),
+        (HASKELL, 'Haskell syntax highlighting'),
     )
 
     submission_type_id = models.UUIDField(primary_key=True,
diff --git a/util/importer.py b/util/importer.py
index 736591c6..bbd4c706 100644
--- a/util/importer.py
+++ b/util/importer.py
@@ -163,13 +163,24 @@ def call_loader(func: Callable) -> None:
     info(f'{func.__name__} is done.')
 
 
+def file_suffix_to_lang_name(suffix: str) -> str:
+    suffix2name = {
+        'hs': 'haskell',
+        's': 'mipsasm'
+    }
+    if suffix not in suffix2name:
+        return suffix
+    return suffix2name[suffix]
+
+
 def do_load_submission_types():
 
     print(
         '''For the following import you need three files:
 
-    1) A .csv file where the columns are: id, name, score, (suffix). No
+    1) A .csv file where the columns are: id, name, score, (file suffix). No
        suffix defaults to .c
+       Supported suffixes: .c , .java , .hs , .s (for mips)
     2) A path to a directory where I can find sample solutions named
         <id>-lsg.c
     3) A path to a directory where I can find HTML files with an accurate
@@ -204,17 +215,18 @@ def do_load_submission_types():
             csv_rows = [row for row in csv.reader(tfile)]
 
         for row in csv_rows:
-            tid, name, score, *lang = (col.strip() for col in row)
+            tid, name, score, *suffix = (col.strip() for col in row)
 
-            if not lang:
-                lang = '.c'
+            if not suffix:
+                suffix = '.c'
             else:
-                lang = lang[0]
+                suffix = suffix[0]
 
-            lang = lang.lower().strip('.')
+            suffix = suffix.lower().strip('.')
+            lang_name = file_suffix_to_lang_name(suffix)
 
             with \
-                open(os.path.join(lsg_dir, tid + '.' + lang),
+                open(os.path.join(lsg_dir, tid + '.' + suffix),
                      encoding='utf-8') as lsg, \
                 open(os.path.join(desc_dir, tid + '.html'),
                      encoding='utf-8') as desc:
@@ -223,7 +235,7 @@ def do_load_submission_types():
                     'description': desc.read(),
                     'solution': lsg.read(),
                     'full_score': int(score),
-                    'programming_language': lang
+                    'programming_language': lang_name
                 }
             _, created = SubmissionType.objects.update_or_create(
                 name=name,
-- 
GitLab


From c7822341595a7a147619f07de9d52c2a5c90b5d0 Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Wed, 15 Aug 2018 14:29:03 +0200
Subject: [PATCH 6/9] Fixed final typings/removed WelcomeJumbotron

---
 frontend/src/components/WelcomeJumbotron.vue  | 66 -------------------
 .../feedback_list/FeedbackSearchOptions.vue   |  2 +-
 .../src/pages/reviewer/ReviewerStartPage.vue  |  5 --
 frontend/src/pages/tutor/TutorStartPage.vue   |  5 --
 frontend/src/store/modules/authentication.ts  |  4 +-
 .../modules/feedback_list/feedback-table.ts   |  2 +-
 .../src/store/modules/submission-notes.ts     |  4 +-
 frontend/src/store/modules/subscriptions.ts   | 16 +++--
 frontend/src/store/mutations.ts               |  7 ++
 frontend/src/store/store.ts                   |  4 +-
 10 files changed, 25 insertions(+), 90 deletions(-)
 delete mode 100644 frontend/src/components/WelcomeJumbotron.vue

diff --git a/frontend/src/components/WelcomeJumbotron.vue b/frontend/src/components/WelcomeJumbotron.vue
deleted file mode 100644
index 687274a6..00000000
--- a/frontend/src/components/WelcomeJumbotron.vue
+++ /dev/null
@@ -1,66 +0,0 @@
-<template>
-    <v-jumbotron :gradient="gradient" dark class="elevation-10" v-if="showJumbotron">
-      <v-btn @click="hide" icon class="hide-btn" absolute>
-        <v-icon>
-          close
-        </v-icon>
-      </v-btn>
-      <v-container fill-height>
-        <v-layout align-center>
-          <v-flex>
-            <h1 class="display-2 mb-2 text-xs-center">Grady</h1>
-            <span class="subheading">
-              The intention of this tool is to simplify the exam correcting process at the
-              University of Goettingen. It is deployed as a
-              <a href="http://www.django-rest-framework.org/" target="_blank">Django Rest</a>
-              backend with a <a href="https://vuejs.org/" target="_blank">Vue.js</a> frontend
-              using the awesome <a href="https://next.vuetifyjs.com/en/" target="_blank">Vuetify</a> library.<br/><br/>
-              To get started with correcting just create a subscription by clicking one of the plus signs in the
-              <i>Your subscriptions</i> card.
-              Subscriptions are the mechanism by which the student submissions are distributed.
-            </span>
-            <v-divider class="my-5"></v-divider>
-            <span class="title mx-4">Check out our
-              <a href="https://gitlab.gwdg.de/j.michal/grady" target="_blank">Git!</a></span>
-            <span class="title mx-4">Why the name
-              <a href="https://www.youtube.com/watch?v=VjdgXqKjHvY" target="_blank">Grady?</a></span>
-          </v-flex>
-        </v-layout>
-      </v-container>
-    </v-jumbotron>
-</template>
-
-<script>
-import { createComputedGetterSetter } from '@/util/helpers'
-import { uiMut } from '@/store/modules/ui'
-
-export default {
-  name: 'welcome-jumbotron',
-  data () {
-    return {
-      gradient: 'to bottom, #1A237E, #5753DD'
-    }
-  },
-  computed: {
-    showJumbotron: createComputedGetterSetter({
-      path: 'ui.showJumbotron',
-      mutation: uiMut.SET_SHOW_JUMBOTRON
-    })
-  },
-  methods: {
-    hide () {
-      this.showJumbotron = false
-    }
-  }
-}
-</script>
-
-<style scoped>
-  a {
-    color: lightgrey;
-    text-decoration: none;
-  }
-  .hide-btn {
-    right: 0;
-  }
-</style>
diff --git a/frontend/src/components/feedback_list/FeedbackSearchOptions.vue b/frontend/src/components/feedback_list/FeedbackSearchOptions.vue
index c5ec661f..c2db7e9c 100644
--- a/frontend/src/components/feedback_list/FeedbackSearchOptions.vue
+++ b/frontend/src/components/feedback_list/FeedbackSearchOptions.vue
@@ -116,7 +116,7 @@ export default {
   },
   methods: {
     loadTutors () {
-      if (this.tutors.length === 0) {
+      if (this.tutors.length === 0 && this.isReviewer) {
         this.$store.dispatch('getTutors')
       }
     }
diff --git a/frontend/src/pages/reviewer/ReviewerStartPage.vue b/frontend/src/pages/reviewer/ReviewerStartPage.vue
index c049278d..ec560458 100644
--- a/frontend/src/pages/reviewer/ReviewerStartPage.vue
+++ b/frontend/src/pages/reviewer/ReviewerStartPage.vue
@@ -1,8 +1,5 @@
 <template>
   <div>
-    <v-flex lg6 md10 xs12 mx-auto class="mt-4">
-      <welcome-jumbotron></welcome-jumbotron>
-    </v-flex>
     <v-layout row wrap>
       <v-flex lg5 md9 xs12>
         <correction-statistics class="ma-4"></correction-statistics>
@@ -17,7 +14,6 @@
 
 <script>
 import CorrectionStatistics from '@/components/CorrectionStatistics'
-import WelcomeJumbotron from '@/components/WelcomeJumbotron'
 import SubscriptionList from '@/components/subscriptions/SubscriptionList'
 import SubmissionTypesOverview from '@/components/SubmissionTypesOverview'
 
@@ -25,7 +21,6 @@ export default {
   components: {
     SubmissionTypesOverview,
     SubscriptionList,
-    WelcomeJumbotron,
     CorrectionStatistics},
   name: 'reviewer-start-page'
 }
diff --git a/frontend/src/pages/tutor/TutorStartPage.vue b/frontend/src/pages/tutor/TutorStartPage.vue
index 3d57117c..b782282b 100644
--- a/frontend/src/pages/tutor/TutorStartPage.vue
+++ b/frontend/src/pages/tutor/TutorStartPage.vue
@@ -1,8 +1,5 @@
 <template>
   <div>
-    <v-flex lg6 md10 xs12 mx-auto class="mt-4">
-      <welcome-jumbotron></welcome-jumbotron>
-    </v-flex>
     <v-layout row wrap>
       <v-flex lg5 md9 xs12>
         <correction-statistics class="ma-4"></correction-statistics>
@@ -18,13 +15,11 @@
 <script>
 import SubscriptionList from '@/components/subscriptions/SubscriptionList'
 import CorrectionStatistics from '@/components/CorrectionStatistics'
-import WelcomeJumbotron from '@/components/WelcomeJumbotron'
 import SubmissionTypesOverview from '@/components/SubmissionTypesOverview'
 
 export default {
   components: {
     SubmissionTypesOverview,
-    WelcomeJumbotron,
     CorrectionStatistics,
     SubscriptionList},
   name: 'tutor-start-page'
diff --git a/frontend/src/store/modules/authentication.ts b/frontend/src/store/modules/authentication.ts
index 1b8381f5..34cb5c88 100644
--- a/frontend/src/store/modules/authentication.ts
+++ b/frontend/src/store/modules/authentication.ts
@@ -90,7 +90,7 @@ const authentication: Module<AuthState, any> = {
     async getJWT (context: ActionContext<AuthState, any>, credentials: Credentials) {
       try {
         const token = await api.fetchJWT(credentials)
-        context.commit(authMut.SET_JWT_TOKEN, token)
+        context.commit(authMut.SET_JWT_TOKEN, token.token)
       } catch (error) {
         let errorMsg
         if (!error.response) {
@@ -110,7 +110,7 @@ const authentication: Module<AuthState, any> = {
       commit(authMut.SET_REFRESHING_TOKEN, true)
       try {
         const token = await api.refreshJWT(state.token)
-        commit(authMut.SET_JWT_TOKEN, token)
+        commit(authMut.SET_JWT_TOKEN, token.token)
       } finally {
         commit(authMut.SET_REFRESHING_TOKEN, false)
         commit(authMut.SET_LAST_TOKEN_REFRESH_TRY)
diff --git a/frontend/src/store/modules/feedback_list/feedback-table.ts b/frontend/src/store/modules/feedback_list/feedback-table.ts
index f005582e..02337c19 100644
--- a/frontend/src/store/modules/feedback_list/feedback-table.ts
+++ b/frontend/src/store/modules/feedback_list/feedback-table.ts
@@ -66,7 +66,7 @@ const feedbackTable: Module<FeedbackTableState, RootState> = {
     },
     async getFeedbackHistory ({getters, commit, dispatch}) {
       let data: [Promise<Feedback[]>, Promise<Assignment[]> | undefined] =
-          [fetchAllFeedback(), getters.isReviewer() ? fetchAllAssignments() : undefined]
+          [fetchAllFeedback(), getters.isReviewer ? fetchAllAssignments() : undefined]
 
       Promise.all<Feedback[], Assignment[] | undefined>(data)
         .then(([feedbacks, assignments]: [Feedback[], Assignment[]?]) => {
diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts
index 32a74341..9a4fe315 100644
--- a/frontend/src/store/modules/submission-notes.ts
+++ b/frontend/src/store/modules/submission-notes.ts
@@ -57,7 +57,7 @@ function initialState (): SubmissionNotesState {
     },
     updatedFeedback: {
       pk: 0,
-      score: 0,
+      score: undefined,
       feedbackLines: {}
     },
     commentsMarkedForDeletion: {}
@@ -86,7 +86,7 @@ const submissionNotes: Module<SubmissionNotesState, RootState> = {
       }, {})
     },
     score: state => {
-      return state.updatedFeedback.score !== null ? state.updatedFeedback.score : state.origFeedback.score
+      return state.updatedFeedback.score !== undefined ? state.updatedFeedback.score : state.origFeedback.score
     },
     workInProgress: state => {
       const openEditor = Object.values(state.ui.showEditorOnLine).reduce((acc, curr) => acc || curr, false)
diff --git a/frontend/src/store/modules/subscriptions.ts b/frontend/src/store/modules/subscriptions.ts
index dab0467f..4888efce 100644
--- a/frontend/src/store/modules/subscriptions.ts
+++ b/frontend/src/store/modules/subscriptions.ts
@@ -35,7 +35,7 @@ function initialState (): SubscriptionsState {
 const MAX_NUMBER_OF_ASSIGNMENTS = 2
 
 // noinspection JSCommentMatchesSignature
-const subscriptions: Module<SubscriptionsState, RootState> = {
+const subscriptionsModule: Module<SubscriptionsState, RootState> = {
   state: initialState(),
   getters: {
     availableTypes (state, getters) {
@@ -93,7 +93,8 @@ const subscriptions: Module<SubscriptionsState, RootState> = {
           'submission_type': []
         }
       }
-      let subscriptionsByStage = getters.availableStages.reduce((acc, curr) => {
+      let subscriptionsByStage = getters.availableStages.reduce((acc: {[p: string]: {[k: string]: Subscription[]}},
+        curr: string) => {
         acc[curr] = subscriptionsByType()
         return acc
       }, {})
@@ -101,7 +102,7 @@ const subscriptions: Module<SubscriptionsState, RootState> = {
         subscriptionsByStage[subscription.feedbackStage][subscription.queryType].push(subscription)
       })
       // sort the resulting arrays in subscriptions lexicographically by their query_keys
-      const sortSubscriptions = subscriptionsByType => Object.values(subscriptionsByType)
+      const sortSubscriptions = (subscriptionsByType: {[k: string]: Subscription[]}) => Object.values(subscriptionsByType)
         .forEach((arr: object[]) => {
           if (arr.length > 1 && arr[0].hasOwnProperty('queryKey')) {
             arr.sort((subA, subB) => {
@@ -117,7 +118,7 @@ const subscriptions: Module<SubscriptionsState, RootState> = {
             })
           }
         })
-      Object.values(subscriptionsByStage).forEach(subscriptionsByType => {
+      Object.values(subscriptionsByStage).forEach((subscriptionsByType: any) => {
         sortSubscriptions(subscriptionsByType)
       })
       return subscriptionsByStage
@@ -166,7 +167,6 @@ const subscriptions: Module<SubscriptionsState, RootState> = {
         commit(subscriptionMuts.SET_SUBSCRIPTION, subscription)
         return subscription
       } catch (err) {
-        console.log(err)
         handleError(err, dispatch, 'Subscribing unsuccessful')
       }
     },
@@ -176,7 +176,6 @@ const subscriptions: Module<SubscriptionsState, RootState> = {
         commit(subscriptionMuts.SET_SUBSCRIPTIONS, subscriptions)
         return subscriptions
       } catch (err) {
-        console.log(err)
         handleError(err, dispatch, 'Unable to fetch subscriptions')
       }
     },
@@ -245,6 +244,7 @@ const subscriptions: Module<SubscriptionsState, RootState> = {
         commit(subscriptionMuts.SET_ACTIVE_SUBSCRIPTION_PK, subscriptionPk)
         let assignmentsPromises = await dispatch('getAssignmentsForActiveSubscription', MAX_NUMBER_OF_ASSIGNMENTS)
         let createdAssignments = []
+        // TODO refactor this since it's very bad to await promises in for loops
         for (let promise of assignmentsPromises) {
           try {
             createdAssignments.push(await promise)
@@ -269,6 +269,7 @@ const subscriptions: Module<SubscriptionsState, RootState> = {
           if (getters.isReviewer) {
             const stageKeyCartesian = cartesian(
               getters.availableStages, getters.availableExamTypeQueryKeys)
+            // @ts-ignore
             return stageKeyCartesian.map(([stage, key]: [string, string]) => {
               dispatch('subscribeTo', {stage, type, key})
             })
@@ -277,6 +278,7 @@ const subscriptions: Module<SubscriptionsState, RootState> = {
         case Subscription.QueryTypeEnum.SubmissionType:
           const stageKeyCartesian = cartesian(
             getters.availableStages, getters.availableSubmissionTypeQueryKeys)
+          // @ts-ignore
           return stageKeyCartesian.map(([stage, key]: [string, string]) => {
             dispatch('subscribeTo', {stage, type, key})
           })
@@ -291,4 +293,4 @@ const subscriptions: Module<SubscriptionsState, RootState> = {
   }
 }
 
-export default subscriptions
+export default subscriptionsModule
diff --git a/frontend/src/store/mutations.ts b/frontend/src/store/mutations.ts
index 8c6f165e..252ead24 100644
--- a/frontend/src/store/mutations.ts
+++ b/frontend/src/store/mutations.ts
@@ -55,6 +55,13 @@ const mutations: MutationTree<RootState> = {
       ...statistics
     }
   },
+  [mut.SET_FEEDBACK] (state, feedback) {
+    Vue.set(state.feedback, feedback.pk, {
+      ...state.feedback[feedback.pk],
+      ...feedback,
+      ofSubmissionType: state.submissionTypes[feedback['ofSubmissionType']]
+    })
+  },
   [mut.UPDATE_SUBMISSION_TYPE] (state, submissionType: SubmissionType) {
     const updatedSubmissionType = {
       ...state.submissionTypes[submissionType.pk],
diff --git a/frontend/src/store/store.ts b/frontend/src/store/store.ts
index 6ab9578e..849519fc 100644
--- a/frontend/src/store/store.ts
+++ b/frontend/src/store/store.ts
@@ -15,7 +15,7 @@ import getters from './getters'
 import mutations from '@/store/mutations'
 import {lastInteraction} from '@/store/plugins/lastInteractionPlugin'
 import {
-  Exam,
+  Exam, Feedback,
   Statistics,
   StudentInfoForListView,
   SubmissionNoType,
@@ -27,6 +27,7 @@ Vue.use(Vuex)
 export interface RootState {
     lastAppInteraction: number
     examTypes: {[pk: string]: Exam}
+    feedback: {[pk: string]: Feedback}
     submissionTypes: {[pk: string]: SubmissionType}
     submissions: {[pk: string]: SubmissionNoType}
     students: {[pk: string]: StudentInfoForListView}
@@ -39,6 +40,7 @@ export function initialState (): RootState {
   return {
     lastAppInteraction: Date.now(),
     examTypes: {},
+    feedback: {},
     submissionTypes: {},
     submissions: {},
     students: {},
-- 
GitLab


From e169cd0ca9f6563193eb933b744aa8c2e3acd76f Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Wed, 15 Aug 2018 15:02:28 +0200
Subject: [PATCH 7/9] Added git install to Dockerfile

This is needed in order to install the forked repo `djangorestframework-camelizer` from github
---
 Dockerfile | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Dockerfile b/Dockerfile
index d9947f19..98f53921 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -18,6 +18,8 @@ RUN apk update \
   && apk add --virtual build-deps gcc python3-dev musl-dev curl \
   && apk add --no-cache postgresql-dev
 
+RUN apk add --no-cache git
+
 COPY requirements.txt .
 RUN pip install -r requirements.txt && rm -rf /root/.cache
 
-- 
GitLab


From 47d6c72d689618edd6d30d56b2ccc8f17deea4f6 Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Sat, 18 Aug 2018 21:00:18 +0200
Subject: [PATCH 8/9] e2e test run in headless mode now in preperation for CI

---
 frontend/nightwatch.json         | 17 +++++++++++++++++
 frontend/nightwatch_globals.js   |  3 +++
 frontend/package.json            |  1 +
 frontend/tests/e2e/specs/test.js |  2 +-
 frontend/yarn.lock               |  4 ++++
 5 files changed, 26 insertions(+), 1 deletion(-)
 create mode 100644 frontend/nightwatch.json
 create mode 100644 frontend/nightwatch_globals.js

diff --git a/frontend/nightwatch.json b/frontend/nightwatch.json
new file mode 100644
index 00000000..261fbe74
--- /dev/null
+++ b/frontend/nightwatch.json
@@ -0,0 +1,17 @@
+{
+  "globals_path": "nightwatch_globals",
+  "selenium": {
+    "cli_args": {
+      "webdriver.gecko.driver": "/usr/bin/geckodriver"
+    }
+  },
+  "test_settings": {
+    "chrome": {
+      "desiredCapabilities": {
+        "chromeOptions": {
+          "args": ["headless"]
+        }
+      }
+    }
+  }
+}
diff --git a/frontend/nightwatch_globals.js b/frontend/nightwatch_globals.js
new file mode 100644
index 00000000..ce0207f9
--- /dev/null
+++ b/frontend/nightwatch_globals.js
@@ -0,0 +1,3 @@
+module.exports = {
+  waitForConditionTimeout: 10000
+}
diff --git a/frontend/package.json b/frontend/package.json
index 3eca1380..dab5a75e 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -28,6 +28,7 @@
     "@types/chai": "^4.1.0",
     "@types/highlight.js": "^9.12.3",
     "@types/mocha": "^5.2.4",
+    "@types/nightwatch": "^0.9.8",
     "@vue/cli-plugin-e2e-nightwatch": "^3.0.0-rc.10",
     "@vue/cli-plugin-eslint": "^3.0.0-rc.10",
     "@vue/cli-plugin-typescript": "^3.0.0-rc.10",
diff --git a/frontend/tests/e2e/specs/test.js b/frontend/tests/e2e/specs/test.js
index 6cfaba24..5720c20e 100644
--- a/frontend/tests/e2e/specs/test.js
+++ b/frontend/tests/e2e/specs/test.js
@@ -6,7 +6,7 @@ module.exports = {
     console.log(`URL: ${process.env.VUE_DEV_SERVER_URL}`)
     browser
       .url(process.env.VUE_DEV_SERVER_URL)
-      .waitForElementVisible('#app', 5000)
+      .waitForElementVisible('#app')
       .assert.elementCount('img', 1)
       .end()
   }
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index 264e7940..6d751e73 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -109,6 +109,10 @@
   version "5.2.5"
   resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.5.tgz#8a4accfc403c124a0bafe8a9fc61a05ec1032073"
 
+"@types/nightwatch@^0.9.8":
+  version "0.9.8"
+  resolved "https://registry.yarnpkg.com/@types/nightwatch/-/nightwatch-0.9.8.tgz#ae38fe9922e1541aabb40730771aec5944961786"
+
 "@types/node@^10.5.2":
   version "10.5.5"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.5.tgz#8e84d24e896cd77b0d4f73df274027e3149ec2ba"
-- 
GitLab


From 3aeeda83bc01a0b62e54d46337fa9211945f04ae Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Sat, 18 Aug 2018 22:34:44 +0200
Subject: [PATCH 9/9] added .asm file ending in import / small bug fix in
 sub-notes store

---
 frontend/src/store/modules/submission-notes.ts | 6 +++---
 util/importer.py                               | 3 ++-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts
index 9a4fe315..ea23fe7b 100644
--- a/frontend/src/store/modules/submission-notes.ts
+++ b/frontend/src/store/modules/submission-notes.ts
@@ -51,7 +51,7 @@ function initialState (): SubmissionNotesState {
     hasOrigFeedback: false,
     origFeedback: {
       pk: 0,
-      score: 0,
+      score: undefined,
       isFinal: false,
       feedbackLines: {}
     },
@@ -158,9 +158,9 @@ const submissionNotes: Module<SubmissionNotesState, RootState> = {
       if (Object.keys(state.updatedFeedback.feedbackLines || {}).length > 0) {
         feedback.feedbackLines = state.updatedFeedback.feedbackLines
       }
-      if (state.origFeedback.score === null && state.updatedFeedback.score === null) {
+      if (state.origFeedback.score === undefined && state.updatedFeedback.score === undefined) {
         throw new Error('You need to give a score.')
-      } else if (state.updatedFeedback.score !== null) {
+      } else if (state.updatedFeedback.score !== undefined) {
         feedback.score = state.updatedFeedback.score
       }
       await dispatch('deleteComments')
diff --git a/util/importer.py b/util/importer.py
index bbd4c706..a1ed1e61 100644
--- a/util/importer.py
+++ b/util/importer.py
@@ -166,7 +166,8 @@ def call_loader(func: Callable) -> None:
 def file_suffix_to_lang_name(suffix: str) -> str:
     suffix2name = {
         'hs': 'haskell',
-        's': 'mipsasm'
+        's': 'mipsasm',
+        'asm': 'mipsasm'
     }
     if suffix not in suffix2name:
         return suffix
-- 
GitLab