diff --git a/core/urls.py b/core/urls.py
index 3e3014d9e5ea1425f870441fc2113bcdf65dd03d..a042f5f9946651b7d72116248c21cf4c2d382504 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 ab0d228b29018576e902cbceb14cc73f50cb2969..3eca1380f8890f6bd5848f6147babccbba8a9cf0 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 a50422e5b5799bb2b2b713abe61e6bc74b4a98f6..1f2d2c1754621abc28fe94617223703cde638433 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 3c70cffe13d7d0a7170377abb966c1f8ff3d2615..d7b5c17f9b4dfd12e2530652c0202e3009ae0e89 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 87153cb736e11e735877857e09ddb49d50ac6c7b..c5f59b166079eeb8359b896965b29dd69611fa88 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 47ca46fc750f2160e3a009bd6ebc70784d4d6323..24babbe0571767cf10747dd62762dc31639f199b 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 12504862fb917894912a7767730ef9d7c507a029..44e8a56d0fbe26b3f04ebf25f8efe608953b5461 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 c3a8118759a09a17153d476b0309666b7b6772d0..1b8381f522c4a6b4a80913376801e3a04297d141 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 c79120eb04ae050c70fb8985270ed4a4f52f1c87..4b89087fcca753be2583ab204f058da653f5885b 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 845fa13d1f51c06a472859b3dfa01b1451502791..f005582e7b067d9e5b3f234206e38a7c7f8408fa 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 f676cf4f4504c4e5b00b71b3d218b9c10300c910..5125c7e45c3f1c3d8541889ab941e4244611bea2 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 f6619be25056b13512efc360503655f90ecfe56e..32a74341c414984f839134504639dd34095e8715 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 79d7a1f8af7f45f1759d3c8c875c33029ae03df2..dab0467fc2d621d48de39d53d3a7084574d71054 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 2cecc601d8df26034b9d98da0fc3547a44d4ce84..b1760cea54c4250af73939c126485a1ff7e768e3 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 96fff066b2ba0082c14444a5045aa94905f07929..8c6f165e89a5f7a9ffa269e972d7f337c0d4b65a 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 7d0659e89e5c9e5f8953197a7b630bb6c04d3d67..7c64f2badffd437f238e5ee8294e33cce8157221 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 db8c0a28389fe3f2578cd55edbdd72345c72fefd..6ab9578e11c1e386f259606d41848921aad93d14 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 b1d6ff277e5a63df194501073a8f1f5ae8b2946e..11f1d8fe8112c194580df5c05456b0aaf6392822 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 7959a1cfd7cbfbe0306e8f38e80e81261877820f..264e79405acb4a6f8e6a7f7ebba0284e52c07288 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 a9a9fbeeae99e1f11d47ff737e508964cc3bab0a..24cbddfa1088a25fdc1e79cffd9aad001db5f6d2 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 5389e4546bde3a408a01b99891de6e67a15e7df6..3a9106717eed1f541c7e93292bbcd96275a5bfd8 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