diff --git a/core/serializers/student.py b/core/serializers/student.py
index 211069c3aeac257ddc3f8a3f73c671c224583366..32f7c3790f64f7a9eae182f120d3ac81e61b0f25 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 6772c660a2c57b4babb356f36c9b2f643dc86c50..3e3014d9e5ea1425f870441fc2113bcdf65dd03d 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 231910e4e09ef7419691769bd9337954ebca1074..ea2fdccbb19751fe267997ece3670c01257f8fbf 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 670854cab4becc9b07050862cbe91601ceb228b8..57bf23bfce5c065d0bf811eb078cc3b1b322fcfb 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 0000000000000000000000000000000000000000..485884b20dd559c7321004ed39ca16bc2daebeec
--- /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 c800f3ea8e347d2dbec47be5b999bd78f2e119d3..59a075aa131f80c4080a3a6c8df151001c6a7bdd 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 1ff127492376e82778d51c91b8d69f44c49e9467..a9a9fbeeae99e1f11d47ff737e508964cc3bab0a 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 0000000000000000000000000000000000000000..29c9f7d385dcf94e0408908be37510818539bcc1
--- /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