From 9ce91de10176aa1f286a03f8f723a0ed2d35df9c Mon Sep 17 00:00:00 2001 From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de> Date: Thu, 9 Aug 2018 11:26:28 +0200 Subject: [PATCH 1/9] Finnaly change max-line-length to 100!!!! --- setup.cfg | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..68c89dfb --- /dev/null +++ b/setup.cfg @@ -0,0 +1,7 @@ +[flake8] + +max-line-length = 100 + +[pep8] + +max-line-length = 100 -- GitLab From c6d65fa990db99779f862e579c6c37249a790283 Mon Sep 17 00:00:00 2001 From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de> Date: Thu, 9 Aug 2018 13:23:17 +0200 Subject: [PATCH 2/9] Added typescript models --- core/serializers/student.py | 2 +- core/urls.py | 23 +- core/views/common_views.py | 9 +- frontend/src/api.ts | 126 ++--- frontend/src/models.ts | 827 +++++++++++++++++++++++++++++++++ frontend/tsconfig.json | 2 +- grady/settings/default.py | 13 +- swagger-api-specification.json | 1 + 8 files changed, 934 insertions(+), 69 deletions(-) create mode 100644 frontend/src/models.ts create mode 100644 swagger-api-specification.json diff --git a/core/serializers/student.py b/core/serializers/student.py index 211069c3..32f7c379 100644 --- a/core/serializers/student.py +++ b/core/serializers/student.py @@ -23,7 +23,7 @@ class StudentInfoSerializer(DynamicFieldsModelSerializer): 'passes_exam') -class StudentInfoSerializerForListView(DynamicFieldsModelSerializer): +class StudentInfoForListViewSerializer(DynamicFieldsModelSerializer): name = serializers.ReadOnlyField(source='user.fullname') user = serializers.ReadOnlyField(source='user.username') user_pk = serializers.ReadOnlyField(source='user.pk') diff --git a/core/urls.py b/core/urls.py index 6772c660..3e3014d9 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,11 @@ -from django.urls import path +from django.urls import path, re_path +from drf_yasg.views import get_schema_view +from drf_yasg import openapi from rest_framework.routers import DefaultRouter +from rest_framework.permissions import IsAdminUser, AllowAny from core import views +from core.permissions import IsReviewer # Create a router and register our viewsets with it. router = DefaultRouter() @@ -20,6 +24,17 @@ router.register('assignment', views.AssignmentApiViewSet) router.register('statistics', views.StatisticsEndpoint, base_name='statistics') router.register('user', views.UserAccountViewSet, base_name='user') +schema_view = get_schema_view( + openapi.Info( + title="Grady API", + default_version='v1', + description="Blub", + ), + # validators=['flex', 'ssv'], + public=True, + permission_classes=(AllowAny,), +) + # regular views that are not viewsets regular_views_urlpatterns = [ path('student-page/', @@ -33,6 +48,12 @@ regular_views_urlpatterns = [ views.get_jwt_expiration_delta, name='jwt-time-delta'), path('export/csv/', views.StudentCSVExport.as_view(), name='export-csv'), + re_path(r'swagger(?P<format>\.json|\.yaml)$', + schema_view.without_ui(cache_timeout=0), name='schema-json'), + re_path(r'swagger/$', schema_view.with_ui('swagger', cache_timeout=0), + name='schema-swagger-ui'), + re_path(r'redoc/$', schema_view.with_ui('redoc', cache_timeout=0), + name='schema-redoc'), ] urlpatterns = [ diff --git a/core/views/common_views.py b/core/views/common_views.py index 231910e4..ea2fdccb 100644 --- a/core/views/common_views.py +++ b/core/views/common_views.py @@ -21,7 +21,7 @@ from core import models from core.models import ExamType, StudentInfo, SubmissionType from core.permissions import IsReviewer, IsStudent, IsTutorOrReviewer from core.serializers import (ExamSerializer, StudentInfoSerializer, - StudentInfoSerializerForListView, + StudentInfoForListViewSerializer, SubmissionNoTypeSerializer, SubmissionSerializer, SubmissionTypeSerializer, TutorSerializer, UserAccountSerializer) @@ -70,7 +70,7 @@ class StudentReviewerApiViewSet(viewsets.ReadOnlyModelViewSet): .prefetch_related('submissions__feedback')\ .prefetch_related('submissions__type')\ .all() - serializer_class = StudentInfoSerializerForListView + serializer_class = StudentInfoForListViewSerializer def _set_students_active(self, active): for student in self.get_queryset(): @@ -143,13 +143,14 @@ class StatisticsEndpoint(viewsets.ViewSet): models.Feedback.objects.aggregate(avg=Avg('score')).get('avg', 0), 'submission_type_progress': - models.SubmissionType.get_annotated_feedback_count().values( + # Queryset is explicitly evaluated so camelizer plugin camelizes it + list(models.SubmissionType.get_annotated_feedback_count().values( 'pk', 'name', 'submission_count', 'feedback_final', 'feedback_in_validation', - 'feedback_in_conflict') + 'feedback_in_conflict')) }) diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 670854ca..57bf23bf 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -1,11 +1,23 @@ -import axios from 'axios' +import axios, {AxiosInstance, AxiosPromise, AxiosResponse} from 'axios' import {Credentials} from '@/store/modules/authentication' - -function addFieldsToUrl ({url, fields = []}: {url: string, fields?: string[]}) { +import { + Assignment, + Exam, + Feedback, FeedbackComment, + JSONWebToken, Statistics, + StudentInfo, + StudentInfoForListView, + Submission, + SubmissionNoType, SubmissionType, + Subscription, + Tutor, UserAccount +} from "@/models"; + +function addFieldsToUrl ({url, fields = []}: {url: string, fields?: string[]}): string { return fields.length > 0 ? url + '?fields=pk,' + fields : url } -function getInstanceBaseUrl () { +function getInstanceBaseUrl (): string { if (process.env.NODE_ENV === 'production') { return `https://${window.location.host}${window.location.pathname}` } else { @@ -13,7 +25,7 @@ function getInstanceBaseUrl () { } } -let ax = axios.create({ +let ax: AxiosInstance = axios.create({ baseURL: getInstanceBaseUrl() }) { @@ -23,43 +35,39 @@ let ax = axios.create({ } } -export async function registerTutor (credentials: Credentials) { - return ax.post('/api/tutor/register/', credentials) +export async function registerTutor (credentials: Credentials): Promise<AxiosResponse<Tutor>> { + return ax.post<Tutor>('/api/tutor/register/', credentials) } -export async function fetchJWT (credentials: Credentials): Promise<string> { +export async function fetchJWT (credentials: Credentials): Promise<JSONWebToken> { const token: string = (await ax.post('/api/get-token/', credentials)).data.token ax.defaults.headers['Authorization'] = `JWT ${token}` - return token + return {token} } -export async function refreshJWT (token: string): Promise<string> { - const newToken: string = (await ax.post('/api/refresh-token/', {token})).data.token - ax.defaults.headers['Authorization'] = `JWT ${newToken}` - return newToken +export async function refreshJWT (oldToken: string): Promise<JSONWebToken> { + const token: string = (await ax.post('/api/refresh-oldToken/', {oldToken})).data.token + ax.defaults.headers['Authorization'] = `JWT ${token}` + return {token} } export async function fetchJWTTimeDelta (): Promise<number> { return (await ax.get('/api/jwt-time-delta/')).data.timeDelta } -export async function fetchUserRole (): Promise<string> { - return (await ax.get('/api/user-role/')).data.role -} - -export async function fetchStudentSelfData () { +export async function fetchStudentSelfData (): Promise<StudentInfo> { return (await ax.get('/api/student-page/')).data } -export async function fetchStudentSubmissions () { +export async function fetchStudentSubmissions (): Promise<Array<Submission>> { return (await ax.get('/api/student-submissions/')).data } -export async function fetchSubmissionFeedbackTests ({pk}: {pk: string}) { +export async function fetchSubmissionFeedbackTests ({pk}: {pk: string}): Promise<SubmissionNoType> { return (await ax.get(`/api/submission/${pk}/`)).data } -export async function fetchAllStudents (fields: string[] = []) { +export async function fetchAllStudents (fields: string[] = []): Promise<Array<StudentInfoForListView>> { const url = addFieldsToUrl({ url: '/api/student/', fields @@ -68,7 +76,7 @@ export async function fetchAllStudents (fields: string[] = []) { } export async function fetchStudent ({pk, fields = []}: -{pk: string, fields?: string[]}) { +{pk: string, fields?: string[]}): Promise<StudentInfoForListView> { const url = addFieldsToUrl({ url: `/api/student/${pk}/`, fields @@ -76,7 +84,7 @@ export async function fetchStudent ({pk, fields = []}: return (await ax.get(url)).data } -export async function fetchAllTutors (fields: string[] = []) { +export async function fetchAllTutors (fields: string[] = []): Promise<Array<Tutor>> { const url = addFieldsToUrl({ url: '/api/tutor/', fields @@ -84,20 +92,20 @@ export async function fetchAllTutors (fields: string[] = []) { return (await ax.get(url)).data } -export async function fetchSubscriptions () { +export async function fetchSubscriptions (): Promise<Array<Subscription>> { return (await ax.get('/api/subscription/')).data } -export async function deactivateSubscription ({pk}: {pk: string}) { +export async function deactivateSubscription ({pk}: {pk: string}): Promise<AxiosResponse<void>> { const url = `/api/subscription/${pk}/` - return (await ax.delete(url)).data + return ax.delete(url) } -export async function fetchSubscription (subscriptionPk: string) { +export async function fetchSubscription (subscriptionPk: string): Promise<Subscription> { return (await ax.get(`/api/subscription/${subscriptionPk}/`)).data } -export async function fetchAllFeedback (fields: string[] = []) { +export async function fetchAllFeedback (fields: string[] = []): Promise<Array<Feedback>> { const url = addFieldsToUrl({ url: '/api/feedback/', fields @@ -105,20 +113,20 @@ export async function fetchAllFeedback (fields: string[] = []) { return (await ax.get(url)).data } -export async function fetchFeedback ({ofSubmission}) { +export async function fetchFeedback ({ofSubmission}: {ofSubmission: string}): Promise<Feedback> { const url = `/api/feedback/${ofSubmission}/` return (await ax.get(url)).data } export async function fetchExamType ({examPk, fields = []}: -{examPk?: string, fields?: string[]}) { +{examPk?: string, fields?: string[]}): Promise<Exam | Array<Exam>> { const url = addFieldsToUrl({ url: `/api/examtype/${examPk !== undefined ? examPk + '/' : ''}`, fields}) return (await ax.get(url)).data } -export async function fetchStatistics (opt = {fields: []}) { +export async function fetchStatistics (opt = {fields: []}): Promise<Statistics> { const url = addFieldsToUrl({ url: '/api/statistics/', fields: opt.fields @@ -126,45 +134,41 @@ export async function fetchStatistics (opt = {fields: []}) { return (await ax.get(url)).data } -interface SubscriptionPayload { - /* eslint-disable */ - query_type: string, - query_key?: string, - feedback_stage?: string - /* eslint-enable */ -} - -export async function subscribeTo (type: string, key: string, stage: string) { - let data: SubscriptionPayload = { - query_type: type +export async function subscribeTo (type: Subscription.QueryTypeEnum, + key: string, + stage: Subscription.FeedbackStageEnum): Promise<Subscription> { + let data: Subscription = { + queryType: type } if (key) { - data.query_key = key + data.queryKey = key } if (stage) { - data.feedback_stage = stage + data.feedbackStage = stage } return (await ax.post('/api/subscription/', data)).data } -export async function createAssignment ({subscription = null, subscriptionPk = ''}) { +export async function createAssignment ( + {subscription = undefined, subscriptionPk = ''}: + {subscription?: Subscription, subscriptionPk?: string}): Promise<Assignment> { const data = { subscription: subscription ? subscription.pk : subscriptionPk } return (await ax.post(`/api/assignment/`, data)).data } -export async function submitFeedbackForAssignment ({feedback}) { +export async function submitFeedbackForAssignment ({feedback}: {feedback: Feedback}): Promise<Feedback> { return (await ax.post('/api/feedback/', feedback)).data } -export async function submitUpdatedFeedback ({feedback}) { - return (await ax.patch(`/api/feedback/${feedback.of_submission}/`, feedback)).data +export async function submitUpdatedFeedback ({feedback}: {feedback: Feedback}): Promise<Feedback> { + return (await ax.patch(`/api/feedback/${feedback.ofSubmission}/`, feedback)).data } -export async function fetchSubmissionTypes (fields = []) { +export async function fetchSubmissionTypes (fields = []): Promise<Array<SubmissionType>> { let url = '/api/submissiontype/' if (fields.length > 0) { url += '?fields=pk,' + fields @@ -172,43 +176,43 @@ export async function fetchSubmissionTypes (fields = []) { return (await ax.get(url)).data } -export async function fetchAllAssignments (fields = []) { +export async function fetchAllAssignments (fields = []): Promise<Array<Assignment>> { const url = addFieldsToUrl({url: '/api/assignment/', fields}) return (await ax.get(url)).data } -export async function deleteAssignment ({assignment}) { +export async function deleteAssignment ({assignment}: {assignment: Assignment}): Promise<AxiosResponse<void>> { const url = `/api/assignment/${assignment.pk}/` - return (await ax.delete(url)).data + return ax.delete(url) } -export async function deleteComment (comment = {pk: undefined}) { +export async function deleteComment (comment = {pk: undefined}): Promise<AxiosResponse<void>> { const url = `/api/feedback-comment/${comment.pk}/` - return (await ax.delete(url)).data + return await ax.delete(url) } -export async function patchComment (comment = {pk: undefined}) { +export async function patchComment (comment = {pk: undefined}): Promise<FeedbackComment> { const url = `/api/feedback-comment/${comment.pk}/` return (await ax.patch(url, comment)).data } -export async function activateAllStudentAccess () { +export async function activateAllStudentAccess (): Promise<AxiosResponse<void>> { return ax.post('/api/student/activate/') } -export async function deactivateAllStudentAccess () { +export async function deactivateAllStudentAccess (): Promise<AxiosResponse<void>> { return ax.post('/api/student/deactivate/') } -export async function changePassword (userPk: string, data) { - return ax.patch(`/api/user/${userPk}/change_password/`, data) +export async function changePassword (userPk: string, data: {password: string}): Promise<UserAccount> { + return (await ax.patch(`/api/user/${userPk}/change_password/`, data)).data } -export async function getOwnUser () { +export async function getOwnUser (): Promise<UserAccount> { return (await ax.get('/api/user/me/')).data } -export async function changeActiveForUser (userPk: string, active: boolean) { +export async function changeActiveForUser (userPk: string, active: boolean): Promise<UserAccount> { return (await ax.patch(`/api/user/${userPk}/change_active/`, {'is_active': active})).data } diff --git a/frontend/src/models.ts b/frontend/src/models.ts new file mode 100644 index 00000000..485884b2 --- /dev/null +++ b/frontend/src/models.ts @@ -0,0 +1,827 @@ +/** + * + * @export + * @interface Assignment + */ +export interface Assignment { + /** + * + * @type {string} + * @memberof Assignment + */ + pk?: string; + /** + * + * @type {string} + * @memberof Assignment + */ + submission?: string; + /** + * + * @type {boolean} + * @memberof Assignment + */ + isDone?: boolean; + /** + * + * @type {string} + * @memberof Assignment + */ + owner?: string; + /** + * + * @type {string} + * @memberof Assignment + */ + stage?: string; +} + +/** + * + * @export + * @interface Exam + */ +export interface Exam { + /** + * + * @type {string} + * @memberof Exam + */ + pk?: string; + /** + * + * @type {string} + * @memberof Exam + */ + moduleReference: string; + /** + * + * @type {number} + * @memberof Exam + */ + totalScore: number; + /** + * + * @type {number} + * @memberof Exam + */ + passScore: number; + /** + * + * @type {boolean} + * @memberof Exam + */ + passOnly?: boolean; +} + +/** + * + * @export + * @interface Feedback + */ +export interface Feedback { + /** + * + * @type {number} + * @memberof Feedback + */ + pk?: number; + /** + * + * @type {string} + * @memberof Feedback + */ + ofSubmission: string; + /** + * + * @type {boolean} + * @memberof Feedback + */ + isFinal?: boolean; + /** + * + * @type {number} + * @memberof Feedback + */ + score?: number; + /** + * + * @type {Array<FeedbackComment>} + * @memberof Feedback + */ + feedbackLines?: {[lineNo: number]: FeedbackComment[]}; + /** + * + * @type {Date} + * @memberof Feedback + */ + created?: string; + /** + * + * @type {string} + * @memberof Feedback + */ + ofSubmissionType?: string; + /** + * + * @type {string} + * @memberof Feedback + */ + feedbackStageForUser?: string; +} + +/** + * + * @export + * @interface FeedbackComment + */ +export interface FeedbackComment { + /** + * + * @type {string} + * @memberof FeedbackComment + */ + pk?: string; + /** + * + * @type {string} + * @memberof FeedbackComment + */ + text: string; + /** + * + * @type {Date} + * @memberof FeedbackComment + */ + created?: string; + /** + * + * @type {string} + * @memberof FeedbackComment + */ + ofTutor?: string; + /** + * + * @type {number} + * @memberof FeedbackComment + */ + ofLine?: number; + /** + * + * @type {boolean} + * @memberof FeedbackComment + */ + visibleToStudent?: boolean; +} + +/** + * + * @export + * @interface Credentials + */ +export interface Credentials { + /** + * + * @type {string} + * @memberof JSONWebToken + */ + username: string; + /** + * + * @type {string} + * @memberof JSONWebToken + */ + password: string; +} + + +export interface JSONWebToken { + token: string +} + +export interface Statistics { + submissionsPerType: number, + submissionsPerStudent: number, + currentMeanScore: number, + submissionTypeProgress: Array<SubmissionTypeProgress> +} + +/** + * + * @export + * @interface StudentInfo + */ +export interface StudentInfo { + /** + * + * @type {string} + * @memberof StudentInfo + */ + pk?: string; + /** + * + * @type {string} + * @memberof StudentInfo + */ + name?: string; + /** + * + * @type {string} + * @memberof StudentInfo + */ + user: string; + /** + * + * @type {string} + * @memberof StudentInfo + */ + matrikelNo?: string; + /** + * + * @type {Exam} + * @memberof StudentInfo + */ + exam: Exam; + /** + * + * @type {Array<SubmissionList>} + * @memberof StudentInfo + */ + submissions: Array<SubmissionList>; + /** + * + * @type {boolean} + * @memberof StudentInfo + */ + passesExam?: boolean; +} + +/** + * + * @export + * @interface StudentInfoForListView + */ +export interface StudentInfoForListView { + /** + * + * @type {string} + * @memberof StudentInfoForListView + */ + pk?: string; + /** + * + * @type {string} + * @memberof StudentInfoForListView + */ + name?: string; + /** + * + * @type {string} + * @memberof StudentInfoForListView + */ + user?: string; + /** + * + * @type {string} + * @memberof StudentInfoForListView + */ + userPk?: string; + /** + * + * @type {string} + * @memberof StudentInfoForListView + */ + exam?: string; + /** + * + * @type {Array<SubmissionNoTextFields>} + * @memberof StudentInfoForListView + */ + submissions: Array<SubmissionNoTextFields>; + /** + * + * @type {string} + * @memberof StudentInfoForListView + */ + matrikelNo?: string; + /** + * + * @type {boolean} + * @memberof StudentInfoForListView + */ + passesExam?: boolean; + /** + * + * @type {boolean} + * @memberof StudentInfoForListView + */ + isActive: boolean; +} + +/** + * + * @export + * @interface Submission + */ +export interface Submission { + /** + * + * @type {string} + * @memberof Submission + */ + pk?: string; + /** + * + * @type {SubmissionType} + * @memberof Submission + */ + type: SubmissionType; + /** + * + * @type {string} + * @memberof Submission + */ + text?: string; + /** + * + * @type {VisibleCommentFeedback} + * @memberof Submission + */ + feedback: VisibleCommentFeedback; + /** + * + * @type {Array<Test>} + * @memberof Submission + */ + tests: Array<Test>; +} + +/** + * + * @export + * @interface SubmissionList + */ +export interface SubmissionList { + /** + * + * @type {string} + * @memberof SubmissionList + */ + pk?: string; + /** + * + * @type {SubmissionTypeList} + * @memberof SubmissionList + */ + type: SubmissionTypeList; + /** + * + * @type {Feedback} + * @memberof SubmissionList + */ + feedback: Feedback; +} + +/** + * + * @export + * @interface SubmissionNoTextFields + */ +export interface SubmissionNoTextFields { + /** + * + * @type {string} + * @memberof SubmissionNoTextFields + */ + pk?: string; + /** + * + * @type {string} + * @memberof SubmissionNoTextFields + */ + type: string; + /** + * + * @type {string} + * @memberof SubmissionNoTextFields + */ + score?: string; + /** + * + * @type {string} + * @memberof SubmissionNoTextFields + */ + _final?: string; + /** + * + * @type {string} + * @memberof SubmissionNoTextFields + */ + fullScore?: string; +} + +/** + * + * @export + * @interface SubmissionNoType + */ +export interface SubmissionNoType { + /** + * + * @type {string} + * @memberof SubmissionNoType + */ + pk?: string; + /** + * + * @type {string} + * @memberof SubmissionNoType + */ + type: string; + /** + * + * @type {string} + * @memberof SubmissionNoType + */ + fullScore?: string; + /** + * + * @type {string} + * @memberof SubmissionNoType + */ + text?: string; + /** + * + * @type {Feedback} + * @memberof SubmissionNoType + */ + feedback: Feedback; + /** + * + * @type {Array<Test>} + * @memberof SubmissionNoType + */ + tests: Array<Test>; +} + +/** + * + * @export + * @interface SubmissionType + */ +export interface SubmissionType { + /** + * + * @type {string} + * @memberof SubmissionType + */ + pk?: string; + /** + * + * @type {string} + * @memberof SubmissionType + */ + name: string; + /** + * + * @type {number} + * @memberof SubmissionType + */ + fullScore?: number; + /** + * + * @type {string} + * @memberof SubmissionType + */ + description: string; + /** + * + * @type {string} + * @memberof SubmissionType + */ + solution: string; + /** + * + * @type {string} + * @memberof SubmissionType + */ + programmingLanguage?: SubmissionType.ProgrammingLanguageEnum; +} + +/** + * @export + * @namespace SubmissionType + */ +export namespace SubmissionType { + /** + * @export + * @enum {string} + */ + export enum ProgrammingLanguageEnum { + C = <any> 'c', + Java = <any> 'java' + } +} + +/** + * + * @export + * @interface SubmissionTypeList + */ +export interface SubmissionTypeList { + /** + * + * @type {string} + * @memberof SubmissionTypeList + */ + pk?: string; + /** + * + * @type {string} + * @memberof SubmissionTypeList + */ + name: string; + /** + * + * @type {number} + * @memberof SubmissionTypeList + */ + fullScore?: number; +} + +export interface SubmissionTypeProgress { + pk?: string, + name: string, + feedbackFinal: number, + feedbackInValidation: number, + feedbackInConflict: number, + submissionCount: number +} + +/** + * + * @export + * @interface Subscription + */ +export interface Subscription { + /** + * + * @type {string} + * @memberof Subscription + */ + pk?: string; + /** + * + * @type {string} + * @memberof Subscription + */ + owner?: string; + /** + * + * @type {string} + * @memberof Subscription + */ + queryType?: Subscription.QueryTypeEnum; + /** + * + * @type {string} + * @memberof Subscription + */ + queryKey?: string; + /** + * + * @type {string} + * @memberof Subscription + */ + feedbackStage?: Subscription.FeedbackStageEnum; + /** + * + * @type {boolean} + * @memberof Subscription + */ + deactivated?: boolean; + /** + * + * @type {string} + * @memberof Subscription + */ + assignments?: string; + /** + * + * @type {string} + * @memberof Subscription + */ + remaining?: string; + /** + * + * @type {string} + * @memberof Subscription + */ + available?: string; +} + +/** + * @export + * @namespace Subscription + */ +export namespace Subscription { + /** + * @export + * @enum {string} + */ + export enum QueryTypeEnum { + Random = <any> 'random', + Student = <any> 'student', + Exam = <any> 'exam', + SubmissionType = <any> 'submission_type' + } + /** + * @export + * @enum {string} + */ + export enum FeedbackStageEnum { + Creation = <any> 'feedback-creation', + Validation = <any> 'feedback-validation', + ConflictResolution = <any> 'feedback-conflict-resolution' + } +} + +/** + * + * @export + * @interface Test + */ +export interface Test { + /** + * + * @type {string} + * @memberof Test + */ + pk?: string; + /** + * + * @type {string} + * @memberof Test + */ + name: string; + /** + * + * @type {string} + * @memberof Test + */ + label: string; + /** + * + * @type {string} + * @memberof Test + */ + annotation: string; +} + +/** + * + * @export + * @interface Tutor + */ +export interface Tutor { + /** + * + * @type {string} + * @memberof Tutor + */ + pk?: string; + /** + * + * @type {string} + * @memberof Tutor + */ + password?: string; + /** + * Designates whether this user should be treated as active. Unselect this instead of deleting accounts. + * @type {boolean} + * @memberof Tutor + */ + isActive?: boolean; + /** + * Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only. + * @type {string} + * @memberof Tutor + */ + username: string; + /** + * + * @type {string} + * @memberof Tutor + */ + feedbackCreated?: string; + /** + * + * @type {string} + * @memberof Tutor + */ + feedbackValidated?: string; +} + +/** + * + * @export + * @interface UserAccount + */ +export interface UserAccount { + /** + * + * @type {string} + * @memberof UserAccount + */ + pk?: string; + /** + * Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only. + * @type {string} + * @memberof UserAccount + */ + username?: string; + /** + * + * @type {string} + * @memberof UserAccount + */ + role?: UserAccount.RoleEnum; + /** + * + * @type {boolean} + * @memberof UserAccount + */ + isAdmin?: boolean; + /** + * + * @type {string} + * @memberof UserAccount + */ + password?: string; +} + +/** + * @export + * @namespace UserAccount + */ +export namespace UserAccount { + /** + * @export + * @enum {string} + */ + export enum RoleEnum { + Student = <any> 'Student', + Tutor = <any> 'Tutor', + Reviewer = <any> 'Reviewer' + } +} + +/** + * + * @export + * @interface VisibleCommentFeedback + */ +export interface VisibleCommentFeedback { + /** + * + * @type {number} + * @memberof VisibleCommentFeedback + */ + pk?: number; + /** + * + * @type {string} + * @memberof VisibleCommentFeedback + */ + ofSubmission: string; + /** + * + * @type {boolean} + * @memberof VisibleCommentFeedback + */ + isFinal?: boolean; + /** + * + * @type {number} + * @memberof VisibleCommentFeedback + */ + score?: number; + /** + * + * @type {string} + * @memberof VisibleCommentFeedback + */ + feedbackLines?: string; + /** + * + * @type {Date} + * @memberof VisibleCommentFeedback + */ + created?: Date; + /** + * + * @type {string} + * @memberof VisibleCommentFeedback + */ + ofSubmissionType?: string; +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index c800f3ea..59a075aa 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "esnext", "module": "esnext", - "strict": false, + "strict": true, "jsx": "preserve", "importHelpers": true, "moduleResolution": "node", diff --git a/grady/settings/default.py b/grady/settings/default.py index 1ff12749..a9a9fbee 100644 --- a/grady/settings/default.py +++ b/grady/settings/default.py @@ -43,6 +43,7 @@ INSTALLED_APPS = [ 'rest_framework', 'corsheaders', 'drf_dynamic_fields', + 'drf_yasg', 'core', ] @@ -138,7 +139,17 @@ REST_FRAMEWORK = { 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', - ) + ), + 'DEFAULT_RENDERER_CLASSES': ( + 'djangorestframework_camel_case.render.CamelCaseJSONRenderer', + ), + + 'DEFAULT_PARSER_CLASSES': ( + 'djangorestframework_camel_case.parser.CamelCaseJSONParser', + ), + # 'JSON_UNDERSCOREIZE': { + # 'no_underscore_before_number': True, + # }, } JWT_AUTH = { diff --git a/swagger-api-specification.json b/swagger-api-specification.json new file mode 100644 index 00000000..29c9f7d3 --- /dev/null +++ b/swagger-api-specification.json @@ -0,0 +1 @@ +{"swagger": "2.0", "info": {"title": "Grady API", "description": "Blub", "version": "v1"}, "host": "localhost:8000", "schemes": ["http"], "basePath": "/api", "consumes": ["application/json"], "produces": ["application/json"], "securityDefinitions": {"basic": {"type": "basic"}}, "security": [{"basic": []}], "paths": {"/assignment/": {"get": {"operationId": "assignment_list", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/Assignment"}}}}, "tags": ["assignment"]}, "post": {"operationId": "assignment_create", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Assignment"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/Assignment"}}}, "tags": ["assignment"]}, "parameters": []}, "/assignment/{assignment_id}/": {"get": {"operationId": "assignment_read", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Assignment"}}}, "tags": ["assignment"]}, "delete": {"operationId": "assignment_delete", "description": "Stop working on the assignment before it is finished", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["assignment"]}, "parameters": [{"name": "assignment_id", "in": "path", "description": "A UUID string identifying this tutor submission assignment.", "required": true, "type": "string", "format": "uuid"}]}, "/examtype/": {"get": {"operationId": "examtype_list", "description": "Gets a list of an individual exam by Id if provided", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/Exam"}}}}, "tags": ["examtype"]}, "parameters": []}, "/examtype/{exam_type_id}/": {"get": {"operationId": "examtype_read", "description": "Gets a list of an individual exam by Id if provided", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Exam"}}}, "tags": ["examtype"]}, "parameters": [{"name": "exam_type_id", "in": "path", "description": "A UUID string identifying this ExamType.", "required": true, "type": "string", "format": "uuid"}]}, "/export/csv/": {"get": {"operationId": "export_csv_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "produces": ["text/csv"], "tags": ["export"]}, "parameters": []}, "/feedback-comment/{comment_id}/": {"patch": {"operationId": "feedback-comment_partial_update", "description": "Gets a list of an individual exam by Id if provided", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/FeedbackComment"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/FeedbackComment"}}}, "tags": ["feedback-comment"]}, "delete": {"operationId": "feedback-comment_delete", "description": "Gets a list of an individual exam by Id if provided", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["feedback-comment"]}, "parameters": [{"name": "comment_id", "in": "path", "description": "A UUID string identifying this Feedback Comment.", "required": true, "type": "string", "format": "uuid"}]}, "/feedback/": {"get": {"operationId": "feedback_list", "description": "Gets a list of an individual exam by Id if provided", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/Feedback"}}}}, "tags": ["feedback"]}, "post": {"operationId": "feedback_create", "description": "Gets a list of an individual exam by Id if provided", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Feedback"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/Feedback"}}}, "tags": ["feedback"]}, "parameters": []}, "/feedback/{submission_pk}/": {"get": {"operationId": "feedback_read", "description": "Gets a list of an individual exam by Id if provided", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Feedback"}}}, "tags": ["feedback"]}, "patch": {"operationId": "feedback_partial_update", "description": "Gets a list of an individual exam by Id if provided", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Feedback"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Feedback"}}}, "tags": ["feedback"]}, "parameters": [{"name": "submission_pk", "in": "path", "required": true, "type": "string"}]}, "/get-token/": {"post": {"operationId": "get-token_create", "description": "API View that receives a POST with a user's username and password.\n\nReturns a JSON Web Token that can be used for authenticated requests.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/JSONWebToken"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/JSONWebToken"}}}, "tags": ["get-token"]}, "parameters": []}, "/jwt-time-delta/": {"get": {"operationId": "jwt-time-delta_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["jwt-time-delta"]}, "parameters": []}, "/refresh-token/": {"post": {"operationId": "refresh-token_create", "description": "API View that returns a refreshed token (with new expiration) based on\nexisting token\n\nIf 'orig_iat' field (original issued-at-time) is found, will first check\nif it's within expiration window, then copy it to the new token", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/RefreshJSONWebToken"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/RefreshJSONWebToken"}}}, "tags": ["refresh-token"]}, "parameters": []}, "/statistics/": {"get": {"operationId": "statistics_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["statistics"]}, "parameters": []}, "/student-page/": {"get": {"operationId": "student-page_read", "description": "Gets all data that belongs to one student", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/StudentInfo"}}}, "tags": ["student-page"]}, "parameters": []}, "/student-submissions/": {"get": {"operationId": "student-submissions_list", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/Submission"}}}}, "tags": ["student-submissions"]}, "parameters": []}, "/student/": {"get": {"operationId": "student_list", "description": "Gets a list of all students without individual submissions", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/StudentInfoForListView"}}}}, "tags": ["student"]}, "parameters": []}, "/student/activate/": {"post": {"operationId": "student_activate", "description": "Gets a list of all students without individual submissions", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/StudentInfoForListView"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/StudentInfoForListView"}}}, "tags": ["student"]}, "parameters": []}, "/student/deactivate/": {"post": {"operationId": "student_deactivate", "description": "Gets a list of all students without individual submissions", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/StudentInfoForListView"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/StudentInfoForListView"}}}, "tags": ["student"]}, "parameters": []}, "/student/{student_id}/": {"get": {"operationId": "student_read", "description": "Gets a list of all students without individual submissions", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/StudentInfoForListView"}}}, "tags": ["student"]}, "parameters": [{"name": "student_id", "in": "path", "description": "A UUID string identifying this Student.", "required": true, "type": "string", "format": "uuid"}]}, "/submission/": {"get": {"operationId": "submission_list", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/SubmissionNoType"}}}}, "tags": ["submission"]}, "parameters": []}, "/submission/{submission_id}/": {"get": {"operationId": "submission_read", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/SubmissionNoType"}}}, "tags": ["submission"]}, "parameters": [{"name": "submission_id", "in": "path", "description": "A UUID string identifying this Submission.", "required": true, "type": "string", "format": "uuid"}]}, "/submissiontype/": {"get": {"operationId": "submissiontype_list", "description": "Gets a list or a detail view of a single SubmissionType", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/SubmissionType"}}}}, "tags": ["submissiontype"]}, "parameters": []}, "/submissiontype/{submission_type_id}/": {"get": {"operationId": "submissiontype_read", "description": "Gets a list or a detail view of a single SubmissionType", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/SubmissionType"}}}, "tags": ["submissiontype"]}, "parameters": [{"name": "submission_type_id", "in": "path", "description": "A UUID string identifying this SubmissionType.", "required": true, "type": "string", "format": "uuid"}]}, "/subscription/": {"get": {"operationId": "subscription_list", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/Subscription"}}}}, "tags": ["subscription"]}, "post": {"operationId": "subscription_create", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Subscription"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/Subscription"}}}, "tags": ["subscription"]}, "parameters": []}, "/subscription/{subscription_id}/": {"get": {"operationId": "subscription_read", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Subscription"}}}, "tags": ["subscription"]}, "delete": {"operationId": "subscription_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["subscription"]}, "parameters": [{"name": "subscription_id", "in": "path", "description": "A UUID string identifying this submission subscription.", "required": true, "type": "string", "format": "uuid"}]}, "/tutor/": {"get": {"operationId": "tutor_list", "description": "Api endpoint for creating, listing, viewing or deleting tutors", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/Tutor"}}}}, "tags": ["tutor"]}, "post": {"operationId": "tutor_create", "description": "Api endpoint for creating, listing, viewing or deleting tutors", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Tutor"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/Tutor"}}}, "tags": ["tutor"]}, "parameters": []}, "/tutor/register/": {"post": {"operationId": "tutor_register", "description": "Api endpoint for creating, listing, viewing or deleting tutors", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Tutor"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/Tutor"}}}, "tags": ["tutor"]}, "parameters": []}, "/tutor/{user_id}/": {"get": {"operationId": "tutor_read", "description": "Api endpoint for creating, listing, viewing or deleting tutors", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Tutor"}}}, "tags": ["tutor"]}, "put": {"operationId": "tutor_update", "description": "Api endpoint for creating, listing, viewing or deleting tutors", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Tutor"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Tutor"}}}, "tags": ["tutor"]}, "patch": {"operationId": "tutor_partial_update", "description": "Api endpoint for creating, listing, viewing or deleting tutors", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/Tutor"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/Tutor"}}}, "tags": ["tutor"]}, "delete": {"operationId": "tutor_delete", "description": "Api endpoint for creating, listing, viewing or deleting tutors", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["tutor"]}, "parameters": [{"name": "user_id", "in": "path", "description": "A UUID string identifying this user.", "required": true, "type": "string", "format": "uuid"}]}, "/user-role/": {"get": {"operationId": "user-role_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["user-role"]}, "parameters": []}, "/user/": {"get": {"operationId": "user_list", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/UserAccount"}}}}, "tags": ["user"]}, "parameters": []}, "/user/me/": {"get": {"operationId": "user_me", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/UserAccount"}}}}, "tags": ["user"]}, "parameters": []}, "/user/{user_id}/": {"get": {"operationId": "user_read", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/UserAccount"}}}, "tags": ["user"]}, "parameters": [{"name": "user_id", "in": "path", "description": "A UUID string identifying this user.", "required": true, "type": "string", "format": "uuid"}]}, "/user/{user_id}/change_active/": {"patch": {"operationId": "user_change_active", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/UserAccount"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/UserAccount"}}}, "tags": ["user"]}, "parameters": [{"name": "user_id", "in": "path", "description": "A UUID string identifying this user.", "required": true, "type": "string", "format": "uuid"}]}, "/user/{user_id}/change_password/": {"patch": {"operationId": "user_change_password", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/UserAccount"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/UserAccount"}}}, "tags": ["user"]}, "parameters": [{"name": "user_id", "in": "path", "description": "A UUID string identifying this user.", "required": true, "type": "string", "format": "uuid"}]}}, "definitions": {"Assignment": {"type": "object", "properties": {"pk": {"title": "Assignment id", "type": "string", "format": "uuid", "readOnly": true}, "submission": {"title": "Submission", "type": "string", "format": "uuid", "readOnly": true}, "isDone": {"title": "Is done", "type": "boolean", "readOnly": true}, "owner": {"title": "Owner", "type": "string", "readOnly": true}, "stage": {"title": "Stage", "type": "string", "readOnly": true}}}, "Exam": {"required": ["moduleReference", "totalScore", "passScore"], "type": "object", "properties": {"pk": {"title": "Exam type id", "type": "string", "format": "uuid", "readOnly": true}, "moduleReference": {"title": "Module reference", "type": "string", "maxLength": 50, "minLength": 1}, "totalScore": {"title": "Total score", "type": "integer", "maximum": 2147483647, "minimum": 0}, "passScore": {"title": "Pass score", "type": "integer", "maximum": 2147483647, "minimum": 0}, "passOnly": {"title": "Pass only", "type": "boolean"}}}, "FeedbackComment": {"required": ["text"], "type": "object", "properties": {"pk": {"title": "Comment id", "type": "string", "format": "uuid", "readOnly": true}, "text": {"title": "Text", "type": "string", "minLength": 1}, "created": {"title": "Created", "type": "string", "format": "date-time", "readOnly": true}, "ofTutor": {"title": "Of tutor", "type": "string", "readOnly": true}, "ofLine": {"title": "Of line", "type": "integer", "maximum": 2147483647, "minimum": 0}, "visibleToStudent": {"title": "Visible to student", "type": "boolean"}}}, "Feedback": {"required": ["ofSubmission"], "type": "object", "properties": {"pk": {"title": "ID", "type": "integer", "readOnly": true}, "ofSubmission": {"title": "Of submission", "type": "string", "format": "uuid"}, "isFinal": {"title": "Is final", "type": "boolean"}, "score": {"title": "Score", "type": "integer", "maximum": 2147483647, "minimum": 0}, "feedbackLines": {"type": "array", "items": {"$ref": "#/definitions/FeedbackComment"}}, "created": {"title": "Created", "type": "string", "format": "date-time", "readOnly": true}, "ofSubmissionType": {"title": "Of submission type", "type": "string", "readOnly": true}, "feedbackStageForUser": {"title": "Feedback stage for user", "type": "string", "readOnly": true}}}, "JSONWebToken": {"required": ["username", "password"], "type": "object", "properties": {"username": {"title": "Username", "type": "string", "minLength": 1}, "password": {"title": "Password", "type": "string", "minLength": 1}}}, "RefreshJSONWebToken": {"required": ["token"], "type": "object", "properties": {"token": {"title": "Token", "type": "string", "minLength": 1}}}, "SubmissionTypeList": {"title": "Type", "required": ["name"], "type": "object", "properties": {"pk": {"title": "Submission type id", "type": "string", "format": "uuid", "readOnly": true}, "name": {"title": "Name", "type": "string", "maxLength": 100, "minLength": 1}, "fullScore": {"title": "Full score", "type": "integer", "maximum": 2147483647, "minimum": 0}}}, "SubmissionList": {"required": ["type", "feedback"], "type": "object", "properties": {"pk": {"title": "Submission id", "type": "string", "format": "uuid", "readOnly": true}, "type": {"$ref": "#/definitions/SubmissionTypeList"}, "feedback": {"$ref": "#/definitions/Feedback"}}}, "StudentInfo": {"required": ["user", "exam", "submissions"], "type": "object", "properties": {"pk": {"title": "Student id", "type": "string", "format": "uuid", "readOnly": true}, "name": {"title": "Name", "type": "string", "readOnly": true}, "user": {"title": "User", "type": "string", "format": "uuid"}, "matrikelNo": {"title": "Matrikel no", "type": "string", "readOnly": true}, "exam": {"$ref": "#/definitions/Exam"}, "submissions": {"type": "array", "items": {"$ref": "#/definitions/SubmissionList"}}, "passesExam": {"title": "Passes exam", "type": "boolean"}}}, "SubmissionType": {"title": "Type", "required": ["name", "description", "solution"], "type": "object", "properties": {"pk": {"title": "Submission type id", "type": "string", "format": "uuid", "readOnly": true}, "name": {"title": "Name", "type": "string", "maxLength": 100, "minLength": 1}, "fullScore": {"title": "Full score", "type": "integer", "maximum": 2147483647, "minimum": 0}, "description": {"title": "Description", "type": "string", "minLength": 1}, "solution": {"title": "Solution", "type": "string", "minLength": 1}, "programmingLanguage": {"title": "Programming language", "type": "string", "enum": ["c", "java"]}}}, "VisibleCommentFeedback": {"title": "Feedback", "required": ["ofSubmission"], "type": "object", "properties": {"pk": {"title": "ID", "type": "integer", "readOnly": true}, "ofSubmission": {"title": "Of submission", "type": "string", "format": "uuid"}, "isFinal": {"title": "Is final", "type": "boolean"}, "score": {"title": "Score", "type": "integer", "maximum": 2147483647, "minimum": 0}, "feedbackLines": {"title": "Feedback lines", "type": "string", "readOnly": true}, "created": {"title": "Created", "type": "string", "format": "date-time", "readOnly": true}, "ofSubmissionType": {"title": "Of submission type", "type": "string", "readOnly": true}}}, "Test": {"required": ["name", "label", "annotation"], "type": "object", "properties": {"pk": {"title": "Test id", "type": "string", "format": "uuid", "readOnly": true}, "name": {"title": "Name", "type": "string", "maxLength": 30, "minLength": 1}, "label": {"title": "Label", "type": "string", "maxLength": 50, "minLength": 1}, "annotation": {"title": "Annotation", "type": "string", "minLength": 1}}}, "Submission": {"required": ["type", "feedback", "tests"], "type": "object", "properties": {"pk": {"title": "Submission id", "type": "string", "format": "uuid", "readOnly": true}, "type": {"$ref": "#/definitions/SubmissionType"}, "text": {"title": "Text", "type": "string"}, "feedback": {"$ref": "#/definitions/VisibleCommentFeedback"}, "tests": {"type": "array", "items": {"$ref": "#/definitions/Test"}}}}, "SubmissionNoTextFields": {"required": ["type"], "type": "object", "properties": {"pk": {"title": "Submission id", "type": "string", "format": "uuid", "readOnly": true}, "type": {"title": "Type", "type": "string", "format": "uuid"}, "score": {"title": "Score", "type": "string", "readOnly": true}, "final": {"title": "Final", "type": "string", "readOnly": true}, "fullScore": {"title": "Full score", "type": "string", "readOnly": true}}}, "StudentInfoForListView": {"required": ["submissions", "isActive"], "type": "object", "properties": {"pk": {"title": "Student id", "type": "string", "format": "uuid", "readOnly": true}, "name": {"title": "Name", "type": "string", "readOnly": true}, "user": {"title": "User", "type": "string", "readOnly": true}, "userPk": {"title": "User pk", "type": "string", "readOnly": true}, "exam": {"title": "Exam", "type": "string", "readOnly": true}, "submissions": {"type": "array", "items": {"$ref": "#/definitions/SubmissionNoTextFields"}}, "matrikelNo": {"title": "Matrikel no", "type": "string", "maxLength": 30, "minLength": 1}, "passesExam": {"title": "Passes exam", "type": "boolean"}, "isActive": {"title": "Is active", "type": "boolean"}}}, "SubmissionNoType": {"required": ["type", "feedback", "tests"], "type": "object", "properties": {"pk": {"title": "Submission id", "type": "string", "format": "uuid", "readOnly": true}, "type": {"title": "Type", "type": "string", "format": "uuid"}, "fullScore": {"title": "Full score", "type": "string", "readOnly": true}, "text": {"title": "Text", "type": "string"}, "feedback": {"$ref": "#/definitions/Feedback"}, "tests": {"type": "array", "items": {"$ref": "#/definitions/Test"}}}}, "Subscription": {"type": "object", "properties": {"pk": {"title": "Subscription id", "type": "string", "format": "uuid", "readOnly": true}, "owner": {"title": "Owner", "type": "string", "readOnly": true}, "queryType": {"title": "Query type", "type": "string", "enum": ["random", "student", "exam", "submission_type"], "default": "random"}, "queryKey": {"title": "Query key", "type": "string", "format": "uuid"}, "feedbackStage": {"title": "Feedback stage", "type": "string", "enum": ["feedback-creation", "feedback-validation", "feedback-conflict-resolution"], "default": "feedback-creation"}, "deactivated": {"title": "Deactivated", "type": "boolean", "readOnly": true}, "assignments": {"title": "Assignments", "type": "string", "readOnly": true}, "remaining": {"title": "Remaining", "type": "string", "readOnly": true}, "available": {"title": "Available", "type": "string", "readOnly": true}}}, "Tutor": {"required": ["username"], "type": "object", "properties": {"pk": {"title": "User id", "type": "string", "format": "uuid", "readOnly": true}, "password": {"title": "Password", "type": "string", "minLength": 1}, "isActive": {"title": "Active", "description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", "type": "boolean"}, "username": {"title": "Username", "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", "type": "string", "pattern": "^[\\w.@+-]+$", "maxLength": 150, "minLength": 1}, "feedbackCreated": {"title": "Feedback created", "type": "string", "readOnly": true}, "feedbackValidated": {"title": "Feedback validated", "type": "string", "readOnly": true}}}, "UserAccount": {"required": ["password"], "type": "object", "properties": {"pk": {"title": "User id", "type": "string", "format": "uuid", "readOnly": true}, "username": {"title": "Username", "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", "type": "string", "readOnly": true, "minLength": 1}, "role": {"title": "Role", "type": "string", "enum": ["Student", "Tutor", "Reviewer"], "readOnly": true}, "isAdmin": {"title": "Is admin", "type": "boolean", "readOnly": true}, "password": {"title": "Password", "type": "string", "maxLength": 128, "minLength": 1}}}}} \ No newline at end of file -- GitLab From 2b988ae3af379045191a2e634f7c66ae439dab97 Mon Sep 17 00:00:00 2001 From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de> Date: Fri, 10 Aug 2018 14:49:28 +0200 Subject: [PATCH 3/9] Refactored code to use new camelCase Api --- frontend/src/api.ts | 32 +-- .../src/components/CorrectionStatistics.vue | 14 +- frontend/src/components/DataExport.vue | 2 +- .../src/components/PasswordChangeDialog.vue | 4 +- frontend/src/components/SubmissionType.vue | 10 +- .../feedback_list/FeedbackTable.vue | 16 +- frontend/src/components/mixins/mixins.js | 2 +- .../components/student/ExamInformation.vue | 6 +- .../src/components/student/SubmissionList.vue | 6 +- .../components/student_list/StudentList.vue | 18 +- .../student_list/StudentListMenu.vue | 2 +- .../submission_notes/SubmissionCorrection.vue | 14 +- .../submission_notes/base/FeedbackComment.vue | 12 +- .../AnnotatedSubmissionBottomToolbar.vue | 4 +- .../subscriptions/SubscriptionForList.vue | 8 +- .../src/components/tutor_list/TutorList.vue | 16 +- frontend/src/models.ts | 213 +++++++++--------- frontend/src/pages/student/StudentLayout.vue | 4 +- .../pages/student/StudentSubmissionPage.vue | 6 +- frontend/src/store/getters.ts | 4 +- frontend/src/store/modules/authentication.ts | 4 +- .../modules/feedback_list/feedback-table.ts | 8 +- .../src/store/modules/submission-notes.ts | 24 +- frontend/src/store/modules/subscriptions.ts | 30 +-- frontend/src/store/mutations.ts | 8 +- frontend/src/store/store.ts | 3 +- 26 files changed, 237 insertions(+), 233 deletions(-) diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 57bf23bf..a50422e5 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -1,17 +1,17 @@ import axios, {AxiosInstance, AxiosPromise, AxiosResponse} from 'axios' import {Credentials} from '@/store/modules/authentication' import { - Assignment, - Exam, - Feedback, FeedbackComment, - JSONWebToken, Statistics, - StudentInfo, - StudentInfoForListView, - Submission, - SubmissionNoType, SubmissionType, - Subscription, - Tutor, UserAccount -} from "@/models"; + Assignment, + Exam, + Feedback, FeedbackComment, + JSONWebToken, Statistics, + StudentInfo, + StudentInfoForListView, + Submission, + SubmissionNoType, SubmissionType, + Subscription, + Tutor, UserAccount +} from '@/models' function addFieldsToUrl ({url, fields = []}: {url: string, fields?: string[]}): string { return fields.length > 0 ? url + '?fields=pk,' + fields : url @@ -135,8 +135,8 @@ export async function fetchStatistics (opt = {fields: []}): Promise<Statistics> } export async function subscribeTo (type: Subscription.QueryTypeEnum, - key: string, - stage: Subscription.FeedbackStageEnum): Promise<Subscription> { + key: string, + stage: Subscription.FeedbackStageEnum): Promise<Subscription> { let data: Subscription = { queryType: type } @@ -152,8 +152,8 @@ export async function subscribeTo (type: Subscription.QueryTypeEnum, } export async function createAssignment ( - {subscription = undefined, subscriptionPk = ''}: - {subscription?: Subscription, subscriptionPk?: string}): Promise<Assignment> { + {subscription = undefined, subscriptionPk = ''}: + {subscription?: Subscription, subscriptionPk?: string}): Promise<Assignment> { const data = { subscription: subscription ? subscription.pk : subscriptionPk } @@ -188,7 +188,7 @@ export async function deleteAssignment ({assignment}: {assignment: Assignment}): export async function deleteComment (comment = {pk: undefined}): Promise<AxiosResponse<void>> { const url = `/api/feedback-comment/${comment.pk}/` - return await ax.delete(url) + return ax.delete(url) } export async function patchComment (comment = {pk: undefined}): Promise<FeedbackComment> { diff --git a/frontend/src/components/CorrectionStatistics.vue b/frontend/src/components/CorrectionStatistics.vue index c6d877b2..a3bf9ead 100644 --- a/frontend/src/components/CorrectionStatistics.vue +++ b/frontend/src/components/CorrectionStatistics.vue @@ -5,25 +5,25 @@ </v-card-title> <div v-if="loaded"> <ul class="inline-list mx-3"> - <li>Submissions per student: <span>{{statistics.submissions_per_student}}</span></li> - <li>Submissions per type: <span>{{statistics.submissions_per_type}}</span></li> + <li>Submissions per student: <span>{{statistics.submissionsPerStudent}}</span></li> + <li>Submissions per type: <span>{{statistics.submissionsPerType}}</span></li> <li>Curr. mean score: <span> - {{statistics.current_mean_score === null ? 'N.A.' : statistics.current_mean_score.toFixed(2)}} + {{statistics.currentMeanScore === null ? 'N.A.' : statistics.currentMeanScore.toFixed(2)}} </span> </li> </ul> <v-divider class="mx-2 my-2"></v-divider> - <div v-for="(progress, index) in statistics.submission_type_progress" :key="index"> + <div v-for="(progress, index) in statistics.submissionTypeProgress" :key="index"> <v-card-title class="py-0"> {{progress.name}} </v-card-title> <div class="mx-3"> <v-progress-linear - :value="progress.feedback_final / progress.submission_count * 100" + :value="progress.feedbackFinal / progress.submissionCount * 100" buffer - :buffer-value="(progress.feedback_in_validation + progress.feedback_final) * 100 / progress.submission_count" - :color="progress.feedback_final === progress.submission_count ? 'green' : 'blue'" + :buffer-value="(progress.feedbackInValidation + progress.feedbackFinal) * 100 / progress.submissionCount" + :color="progress.feedbackFinal === progress.submissionCount ? 'green' : 'blue'" /> </div> </div> diff --git a/frontend/src/components/DataExport.vue b/frontend/src/components/DataExport.vue index 451fd950..269d2a27 100644 --- a/frontend/src/components/DataExport.vue +++ b/frontend/src/components/DataExport.vue @@ -125,7 +125,7 @@ export default { return students.map(studentData => { return { ...studentData, - Matrikel: this.$store.state.studentMap[studentData.Matrikel].matrikel_no, + Matrikel: this.$store.state.studentMap[studentData.Matrikel].matrikelNo, Name: this.$store.state.studentMap[studentData.Matrikel].name } }) diff --git a/frontend/src/components/PasswordChangeDialog.vue b/frontend/src/components/PasswordChangeDialog.vue index 60d1b072..03e4758a 100644 --- a/frontend/src/components/PasswordChangeDialog.vue +++ b/frontend/src/components/PasswordChangeDialog.vue @@ -67,8 +67,8 @@ export default { methods: { submitChange () { const data = { - old_password: this.currentPassword, - new_password: this.newPassword + oldPassword: this.currentPassword, + newPassword: this.newPassword } changePassword(this.userPk, data).then(() => { this.$notify({ diff --git a/frontend/src/components/SubmissionType.vue b/frontend/src/components/SubmissionType.vue index e952ee27..253ebad3 100644 --- a/frontend/src/components/SubmissionType.vue +++ b/frontend/src/components/SubmissionType.vue @@ -1,7 +1,7 @@ <template> <v-layout column> <v-card> - <v-card-title class="title mb-2">{{ name }} - Full score: {{ full_score }}</v-card-title> + <v-card-title class="title mb-2">{{ name }} - Full score: {{ fullScore }}</v-card-title> <v-expansion-panel expand> <v-expansion-panel-content v-for="(item, i) in typeItems" @@ -22,7 +22,7 @@ <v-flex v-else-if="item.title === 'Solution'"> <pre class="elevation-2 solution-code pl-2" - :class="programming_language" + :class="programmingLanguage" ><span v-html="highlightedSolution"></span></pre> </v-flex> </v-expansion-panel-content> @@ -49,11 +49,11 @@ export default { type: String, required: true }, - full_score: { + fullScore: { type: Number, required: true }, - programming_language: { + programmingLanguage: { type: String, default: 'c' }, @@ -93,7 +93,7 @@ export default { } }, highlightedSolution () { - return highlight(this.programming_language, this.solution, true).value + return highlight(this.programmingLanguage, this.solution, true).value }, backgroundColor () { return this.darkMode ? 'grey' : '#F3F3F3' diff --git a/frontend/src/components/feedback_list/FeedbackTable.vue b/frontend/src/components/feedback_list/FeedbackTable.vue index c75bf049..c00dc781 100644 --- a/frontend/src/components/feedback_list/FeedbackTable.vue +++ b/frontend/src/components/feedback_list/FeedbackTable.vue @@ -21,14 +21,14 @@ hide-actions > <template slot="items" slot-scope="props"> - <tr @click="showSubmission(props.item.of_submission)" + <tr @click="showSubmission(props.item.ofSubmission)" class="feedback-row" > - <td>{{props.item.of_submission_type}}</td> + <td>{{props.item.ofSubmissionType}}</td> <td>{{props.item.score}}</td> <td>{{new Date(props.item.created).toLocaleString()}}</td> <td> - <v-icon v-if="props.item.is_final">check</v-icon> + <v-icon v-if="props.item.isFinal">check</v-icon> <v-icon v-else>clear</v-icon> </td> </tr> @@ -72,7 +72,7 @@ export default { { text: 'Type', align: 'left', - value: 'of_submission_type' + value: 'ofSubmissionType' }, { text: 'score', @@ -110,8 +110,8 @@ export default { prefetchFilteredItems (items) { if (items.length < this.prefetchWhenLessItems) { for (let item of items) { - if (!this.$store.state.submissions[item.of_submission]) { - this.prefetchSubmission(item.of_submission) + if (!this.$store.state.submissions[item.ofSubmission]) { + this.prefetchSubmission(item.ofSubmission) } } } @@ -130,10 +130,10 @@ export default { } }, checkFinal (feedback) { - return this.showFinal ? true : !feedback.is_final + return this.showFinal ? true : !feedback.isFinal }, commentFilter (feedback, search, filter) { - return Object.values(feedback.feedback_lines).some(line => line.some(comment => { + return Object.values(feedback.feedbackLines).some(line => line.some(comment => { return filter(comment.text, search) })) }, diff --git a/frontend/src/components/mixins/mixins.js b/frontend/src/components/mixins/mixins.js index b1365f3a..4bd52cef 100644 --- a/frontend/src/components/mixins/mixins.js +++ b/frontend/src/components/mixins/mixins.js @@ -6,7 +6,7 @@ export var parseCSVMapMixin = { return lines.reduce((acc, curr) => { if (curr) { let [key, matrikelNo, name] = curr.split(';') - acc[key] = {matrikel_no: matrikelNo, name} + acc[key] = {matrikelNo: matrikelNo, name} } return acc }, {}) diff --git a/frontend/src/components/student/ExamInformation.vue b/frontend/src/components/student/ExamInformation.vue index 0cdf58d9..6c079e8f 100644 --- a/frontend/src/components/student/ExamInformation.vue +++ b/frontend/src/components/student/ExamInformation.vue @@ -3,18 +3,18 @@ <tbody> <tr> <th>Module</th> - <td>{{ exam.module_reference }}</td> + <td>{{ exam.moduleReference }}</td> </tr> <tr> <th>Pass score</th> - <td>{{ exam.pass_score }}</td> + <td>{{ exam.passScore }}</td> </tr> <tr v-if="exam.passOnly"> <th>Pass only exam!</th> </tr> <tr> <th>Total score</th> - <td>{{ exam.total_score }}</td> + <td>{{ exam.totalScore }}</td> </tr> </tbody> </table> diff --git a/frontend/src/components/student/SubmissionList.vue b/frontend/src/components/student/SubmissionList.vue index 835853d8..b7dd7052 100644 --- a/frontend/src/components/student/SubmissionList.vue +++ b/frontend/src/components/student/SubmissionList.vue @@ -9,7 +9,7 @@ <template slot="items" slot-scope="props"> <td>{{ props.item.type.name }}</td> <td class="text-xs-right">{{ props.item.feedback ? props.item.feedback.score : 'N/A'}}</td> - <td class="text-xs-right">{{ props.item.type.full_score }}</td> + <td class="text-xs-right">{{ props.item.type.fullScore }}</td> <td class="text-xs-right"> <v-btn :to="`/submission/${props.item.type.pk}`" color="orange lighten-2"> <v-icon>chevron_right</v-icon> @@ -43,7 +43,7 @@ export default { { text: 'Maximum Score', align: 'right', - value: 'type.full_score' + value: 'type.fullScore' }, { text: 'View', @@ -64,7 +64,7 @@ export default { return this.submissions.map(a => a.feedback && a.feedback.score).reduce((a, b) => a + b) }, sumFullScore () { - return this.submissions.map(a => a.type.full_score).reduce((a, b) => a + b) + return this.submissions.map(a => a.type.fullScore).reduce((a, b) => a + b) }, pointRatio () { return ((this.sumScore / this.sumFullScore) * 100).toFixed(2) diff --git a/frontend/src/components/student_list/StudentList.vue b/frontend/src/components/student_list/StudentList.vue index 59b22e35..1e2c0e72 100644 --- a/frontend/src/components/student_list/StudentList.vue +++ b/frontend/src/components/student_list/StudentList.vue @@ -50,10 +50,10 @@ {{props.item.name}} <v-tooltip top> <template slot="activator"> - <v-icon small v-if="!props.item.is_active">lock</v-icon> + <v-icon small v-if="!props.item.isActive">lock</v-icon> <v-icon small v-else>lock_open</v-icon> </template> - <span v-if="!props.item.is_active">Student doesn't have access.</span> + <span v-if="!props.item.isActive">Student doesn't have access.</span> <span v-else>Student has access.</span> </v-tooltip> </td> @@ -87,14 +87,14 @@ <v-card flat> <v-card-text> <v-btn @click="changeActiveStatus(props.item)"> - {{props.item.is_active ? 'Revoke access' : 'Grant access'}} + {{props.item.isActive ? 'Revoke access' : 'Grant access'}} </v-btn> <ul class="student-info-list"> <li> <b>Modul:</b> {{props.item.exam}} </li> <li> - <b>MatrikelNr:</b> {{props.item.matrikel_no}} + <b>MatrikelNr:</b> {{props.item.matrikelNo}} </li> </ul> </v-card-text> @@ -163,11 +163,11 @@ export default { return { pk: student.pk, user: student.user, - user_pk: student.user_pk, + userPk: student.userPk, exam: student.exam, name: student.name, - is_active: student.is_active, - matrikel_no: student.matrikel_no, + isActive: student.isActive, + matrikelNo: student.matrikelNo, ...this.reduceArrToDict(student.submissions, 'type'), total: this.sumSubmissionScores(student.submissions) } @@ -187,8 +187,8 @@ export default { }, {}) }, changeActiveStatus (student) { - changeActiveForUser(student.user_pk, !student.is_active).then(() => { - this.getStudents({studentPks: [student.pk], fields: ['is_active']}) + changeActiveForUser(student.userPk, !student.isActive).then(() => { + this.getStudents({studentPks: [student.pk], fields: ['isActive']}) }).catch(() => { this.$notify({ title: 'Error', diff --git a/frontend/src/components/student_list/StudentListMenu.vue b/frontend/src/components/student_list/StudentListMenu.vue index b60445fb..ec0129b0 100644 --- a/frontend/src/components/student_list/StudentListMenu.vue +++ b/frontend/src/components/student_list/StudentListMenu.vue @@ -20,7 +20,7 @@ export default { computed: { studentsActive () { const firstStudent = Object.values(this.$store.state.students)[0] - return firstStudent ? firstStudent.is_active === true : false + return firstStudent ? firstStudent.isActive === true : false }, items () { return [ diff --git a/frontend/src/components/submission_notes/SubmissionCorrection.vue b/frontend/src/components/submission_notes/SubmissionCorrection.vue index db73d18d..3c70cffe 100644 --- a/frontend/src/components/submission_notes/SubmissionCorrection.vue +++ b/frontend/src/components/submission_notes/SubmissionCorrection.vue @@ -17,10 +17,10 @@ <feedback-comment v-for="(comment, index) in origFeedback[lineNo]" v-bind="comment" - :visible_to_student="updatedFeedback[lineNo] ? false : comment.visible_to_student" + :visibleToStudent="updatedFeedback[lineNo] ? false : comment.visibleToStudent" :line-no="lineNo" :key="index" - :deletable="comment.of_tutor === user || isReviewer" + :deletable="comment.ofTutor === user || isReviewer" @click.native="toggleEditorOnLine(lineNo, comment)" /> </div> @@ -46,7 +46,7 @@ class="mt-1 elevation-1" slot="footer" :loading="loading" - :fullScore="submissionObj['full_score']" + :fullScore="submissionObj['fullScore']" :skippable="assignment !== undefined" :feedback="feedbackObj ? feedbackObj : {}" @submitFeedback="submitFeedback" @@ -99,8 +99,8 @@ export default { user: state => state.authentication.user.username, showEditorOnLine: state => state.submissionNotes.ui.showEditorOnLine, selectedComment: state => state.submissionNotes.ui.selectedCommentOnLine, - origFeedback: state => state.submissionNotes.origFeedback.feedback_lines, - updatedFeedback: state => state.submissionNotes.updatedFeedback.feedback_lines, + origFeedback: state => state.submissionNotes.origFeedback.feedbackLines, + updatedFeedback: state => state.submissionNotes.updatedFeedback.feedbackLines, showFeedback: state => state.submissionNotes.ui.showFeedback }), ...mapGetters([ @@ -146,8 +146,8 @@ export default { }, shortPollOrigFeedback () { this.feedbackShortPollInterval = setInterval(() => { - if (this.feedbackObj && this.feedbackObj.of_submission) { - this.$store.dispatch('getFeedback', {ofSubmission: this.feedbackObj.of_submission}).then(feedback => { + if (this.feedbackObj && this.feedbackObj.ofSubmission) { + this.$store.dispatch('getFeedback', {ofSubmission: this.feedbackObj.ofSubmission}).then(feedback => { this.$store.commit(subNotesNamespace(subNotesMut.SET_ORIG_FEEDBACK), feedback) }) } diff --git a/frontend/src/components/submission_notes/base/FeedbackComment.vue b/frontend/src/components/submission_notes/base/FeedbackComment.vue index 5b29b56c..989689e6 100644 --- a/frontend/src/components/submission_notes/base/FeedbackComment.vue +++ b/frontend/src/components/submission_notes/base/FeedbackComment.vue @@ -2,10 +2,10 @@ <div class="dialog-box"> <div class="body elevation-1" :style="{borderColor: borderColor, backgroundColor}"> <span class="tip tip-up" :style="{borderBottomColor: borderColor}"></span> - <span v-if="of_tutor" class="of-tutor">Of tutor: {{of_tutor}}</span> + <span v-if="ofTutor" class="of-tutor">Of tutor: {{ofTutor}}</span> <span class="comment-created">{{parsedCreated}}</span> - <div class="visibility-icon" v-if="show_visibility_icon"> - <v-tooltip top v-if="visible_to_student" size="20px"> + <div class="visibility-icon" v-if="showVisibilityIcon"> + <v-tooltip top v-if="visibleToStudent" size="20px"> <v-icon slot="activator" size="20px" @@ -54,7 +54,7 @@ export default { type: String, required: false }, - of_tutor: { + ofTutor: { type: String, required: false }, @@ -66,11 +66,11 @@ export default { type: Boolean, default: false }, - visible_to_student: { + visibleToStudent: { type: Boolean, default: true }, - show_visibility_icon: { + showVisibilityIcon: { type: Boolean, default: true } diff --git a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue index 4c03f1a0..c26aea45 100644 --- a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue +++ b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue @@ -108,8 +108,8 @@ export default { if (this.$route.name === 'subscription') { return !this.$store.getters['submissionNotes/isFeedbackCreation'] || this.$store.getters.isReviewer } else { - if (this.feedback.hasOwnProperty('is_final')) { - return this.feedback.is_final + if (this.feedback.hasOwnProperty('isFinal')) { + return this.feedback.isFinal } else { return !this.$store.getters['submissionNotes/isFeedbackCreation'] || this.$store.getters.isReviewer } diff --git a/frontend/src/components/subscriptions/SubscriptionForList.vue b/frontend/src/components/subscriptions/SubscriptionForList.vue index 867eaa57..b8284c04 100644 --- a/frontend/src/components/subscriptions/SubscriptionForList.vue +++ b/frontend/src/components/subscriptions/SubscriptionForList.vue @@ -25,11 +25,11 @@ export default { types: String, required: true }, - feedback_stage: { + feedbackStage: { type: String, required: true }, - query_type: { + queryType: { type: String, required: true }, @@ -41,14 +41,14 @@ export default { type: Array, required: true }, - query_key: { + queryKey: { type: String } }, computed: { name () { return this.$store.getters.resolveSubscriptionKeyToName( - {query_key: this.query_key, query_type: this.query_type}) + {queryKey: this.queryKey, queryType: this.queryType}) }, active () { return !!this.available || this.assignments.length > 0 diff --git a/frontend/src/components/tutor_list/TutorList.vue b/frontend/src/components/tutor_list/TutorList.vue index 9e0d2f67..2b9abb0a 100644 --- a/frontend/src/components/tutor_list/TutorList.vue +++ b/frontend/src/components/tutor_list/TutorList.vue @@ -9,16 +9,16 @@ > <template slot="items" slot-scope="props"> <td>{{props.item.username}}</td> - <td class="text-xs-right">{{props.item.feedback_created}}</td> - <td class="text-xs-right">{{props.item.feedback_validated}}</td> + <td class="text-xs-right">{{props.item.feedbackCreated}}</td> + <td class="text-xs-right">{{props.item.feedbackValidated}}</td> <td class="text-xs-right"> <v-btn icon @click="changeActiveStatus(props.item)"> <v-tooltip top> <template slot="activator"> - <v-icon small v-if="!props.item.is_active">lock</v-icon> + <v-icon small v-if="!props.item.isActive">lock</v-icon> <v-icon small v-else>lock_open</v-icon> </template> - <span v-if="!props.item.is_active">Grant access</span> + <span v-if="!props.item.isActive">Grant access</span> <span v-else>Revoke access</span> </v-tooltip> </v-btn> @@ -46,17 +46,17 @@ export default { { text: '# created', align: 'right', - value: 'feedback_created' + value: 'feedbackCreated' }, { text: '# validated', align: 'right', - value: 'feedback_validated' + value: 'feedbackValidated' }, { text: 'Has Access', align: 'right', - value: 'is_active' + value: 'isActive' } ] } @@ -71,7 +71,7 @@ export default { 'getTutors' ]), changeActiveStatus (tutor) { - changeActiveForUser(tutor.pk, !tutor.is_active).then(() => { + changeActiveForUser(tutor.pk, !tutor.isActive).then(() => { this.getTutors() }).catch(() => { this.$notify({ diff --git a/frontend/src/models.ts b/frontend/src/models.ts index 485884b2..87153cb7 100644 --- a/frontend/src/models.ts +++ b/frontend/src/models.ts @@ -9,31 +9,31 @@ export interface Assignment { * @type {string} * @memberof Assignment */ - pk?: string; + pk?: string /** * * @type {string} * @memberof Assignment */ - submission?: string; + submission?: string /** * * @type {boolean} * @memberof Assignment */ - isDone?: boolean; + isDone?: boolean /** * * @type {string} * @memberof Assignment */ - owner?: string; + owner?: string /** * * @type {string} * @memberof Assignment */ - stage?: string; + stage?: string } /** @@ -47,31 +47,31 @@ export interface Exam { * @type {string} * @memberof Exam */ - pk?: string; + pk?: string /** * * @type {string} * @memberof Exam */ - moduleReference: string; + moduleReference: string /** * * @type {number} * @memberof Exam */ - totalScore: number; + totalScore: number /** * * @type {number} * @memberof Exam */ - passScore: number; + passScore: number /** * * @type {boolean} * @memberof Exam */ - passOnly?: boolean; + passOnly?: boolean } /** @@ -85,49 +85,49 @@ export interface Feedback { * @type {number} * @memberof Feedback */ - pk?: number; + pk?: number /** * * @type {string} * @memberof Feedback */ - ofSubmission: string; + ofSubmission: string /** * * @type {boolean} * @memberof Feedback */ - isFinal?: boolean; + isFinal?: boolean /** * * @type {number} * @memberof Feedback */ - score?: number; + score?: number /** * * @type {Array<FeedbackComment>} * @memberof Feedback */ - feedbackLines?: {[lineNo: number]: FeedbackComment[]}; + feedbackLines?: {[lineNo: number]: FeedbackComment[]} /** * * @type {Date} * @memberof Feedback */ - created?: string; + created?: string /** * * @type {string} * @memberof Feedback */ - ofSubmissionType?: string; + ofSubmissionType?: string /** * * @type {string} * @memberof Feedback */ - feedbackStageForUser?: string; + feedbackStageForUser?: string } /** @@ -141,37 +141,37 @@ export interface FeedbackComment { * @type {string} * @memberof FeedbackComment */ - pk?: string; + pk?: string /** * * @type {string} * @memberof FeedbackComment */ - text: string; + text: string /** * * @type {Date} * @memberof FeedbackComment */ - created?: string; + created?: string /** * * @type {string} * @memberof FeedbackComment */ - ofTutor?: string; + ofTutor?: string /** * * @type {number} * @memberof FeedbackComment */ - ofLine?: number; + ofLine?: number /** * * @type {boolean} * @memberof FeedbackComment */ - visibleToStudent?: boolean; + visibleToStudent?: boolean } /** @@ -185,13 +185,13 @@ export interface Credentials { * @type {string} * @memberof JSONWebToken */ - username: string; + username: string /** * * @type {string} * @memberof JSONWebToken */ - password: string; + password: string } @@ -217,43 +217,43 @@ export interface StudentInfo { * @type {string} * @memberof StudentInfo */ - pk?: string; + pk?: string /** * * @type {string} * @memberof StudentInfo */ - name?: string; + name?: string /** * * @type {string} * @memberof StudentInfo */ - user: string; + user: string /** * * @type {string} * @memberof StudentInfo */ - matrikelNo?: string; + matrikelNo?: string /** * * @type {Exam} * @memberof StudentInfo */ - exam: Exam; + exam: Exam /** * * @type {Array<SubmissionList>} * @memberof StudentInfo */ - submissions: Array<SubmissionList>; + submissions: Array<SubmissionList> /** * * @type {boolean} * @memberof StudentInfo */ - passesExam?: boolean; + passesExam?: boolean } /** @@ -267,55 +267,55 @@ export interface StudentInfoForListView { * @type {string} * @memberof StudentInfoForListView */ - pk?: string; + pk?: string /** * * @type {string} * @memberof StudentInfoForListView */ - name?: string; + name?: string /** * * @type {string} * @memberof StudentInfoForListView */ - user?: string; + user?: string /** * * @type {string} * @memberof StudentInfoForListView */ - userPk?: string; + userPk?: string /** * * @type {string} * @memberof StudentInfoForListView */ - exam?: string; + exam?: string /** * * @type {Array<SubmissionNoTextFields>} * @memberof StudentInfoForListView */ - submissions: Array<SubmissionNoTextFields>; + submissions: Array<SubmissionNoTextFields> /** * * @type {string} * @memberof StudentInfoForListView */ - matrikelNo?: string; + matrikelNo?: string /** * * @type {boolean} * @memberof StudentInfoForListView */ - passesExam?: boolean; + passesExam?: boolean /** * * @type {boolean} * @memberof StudentInfoForListView */ - isActive: boolean; + isActive: boolean } /** @@ -329,31 +329,31 @@ export interface Submission { * @type {string} * @memberof Submission */ - pk?: string; + pk?: string /** * * @type {SubmissionType} * @memberof Submission */ - type: SubmissionType; + type: SubmissionType /** * * @type {string} * @memberof Submission */ - text?: string; + text?: string /** * * @type {VisibleCommentFeedback} * @memberof Submission */ - feedback: VisibleCommentFeedback; + feedback: VisibleCommentFeedback /** * * @type {Array<Test>} * @memberof Submission */ - tests: Array<Test>; + tests: Array<Test> } /** @@ -367,19 +367,19 @@ export interface SubmissionList { * @type {string} * @memberof SubmissionList */ - pk?: string; + pk?: string /** * * @type {SubmissionTypeList} * @memberof SubmissionList */ - type: SubmissionTypeList; + type: SubmissionTypeList /** * * @type {Feedback} * @memberof SubmissionList */ - feedback: Feedback; + feedback: Feedback } /** @@ -393,31 +393,31 @@ export interface SubmissionNoTextFields { * @type {string} * @memberof SubmissionNoTextFields */ - pk?: string; + pk?: string /** * * @type {string} * @memberof SubmissionNoTextFields */ - type: string; + type: string /** * * @type {string} * @memberof SubmissionNoTextFields */ - score?: string; + score?: string /** * * @type {string} * @memberof SubmissionNoTextFields */ - _final?: string; + final?: string /** * * @type {string} * @memberof SubmissionNoTextFields */ - fullScore?: string; + fullScore?: string } /** @@ -431,37 +431,37 @@ export interface SubmissionNoType { * @type {string} * @memberof SubmissionNoType */ - pk?: string; + pk?: string /** * * @type {string} * @memberof SubmissionNoType */ - type: string; + type: string /** * * @type {string} * @memberof SubmissionNoType */ - fullScore?: string; + fullScore?: string /** * * @type {string} * @memberof SubmissionNoType */ - text?: string; + text?: string /** * * @type {Feedback} * @memberof SubmissionNoType */ - feedback: Feedback; + feedback: Feedback /** * * @type {Array<Test>} * @memberof SubmissionNoType */ - tests: Array<Test>; + tests: Array<Test> } /** @@ -475,37 +475,37 @@ export interface SubmissionType { * @type {string} * @memberof SubmissionType */ - pk?: string; + pk?: string /** * * @type {string} * @memberof SubmissionType */ - name: string; + name: string /** * * @type {number} * @memberof SubmissionType */ - fullScore?: number; + fullScore?: number /** * * @type {string} * @memberof SubmissionType */ - description: string; + description: string /** * * @type {string} * @memberof SubmissionType */ - solution: string; + solution: string /** * * @type {string} * @memberof SubmissionType */ - programmingLanguage?: SubmissionType.ProgrammingLanguageEnum; + programmingLanguage?: SubmissionType.ProgrammingLanguageEnum } /** @@ -534,27 +534,28 @@ export interface SubmissionTypeList { * @type {string} * @memberof SubmissionTypeList */ - pk?: string; + pk?: string + /** * * @type {string} * @memberof SubmissionTypeList */ - name: string; + name: string /** * * @type {number} * @memberof SubmissionTypeList */ - fullScore?: number; + fullScore?: number } export interface SubmissionTypeProgress { - pk?: string, - name: string, - feedbackFinal: number, - feedbackInValidation: number, - feedbackInConflict: number, + pk?: string + name: string + feedbackFinal: number + feedbackInValidation: number + feedbackInConflict: number submissionCount: number } @@ -569,55 +570,55 @@ export interface Subscription { * @type {string} * @memberof Subscription */ - pk?: string; + pk?: string /** * * @type {string} * @memberof Subscription */ - owner?: string; + owner?: string /** * * @type {string} * @memberof Subscription */ - queryType?: Subscription.QueryTypeEnum; + queryType?: Subscription.QueryTypeEnum /** * * @type {string} * @memberof Subscription */ - queryKey?: string; + queryKey?: string /** * * @type {string} * @memberof Subscription */ - feedbackStage?: Subscription.FeedbackStageEnum; + feedbackStage?: Subscription.FeedbackStageEnum /** * * @type {boolean} * @memberof Subscription */ - deactivated?: boolean; + deactivated?: boolean /** * * @type {string} * @memberof Subscription */ - assignments?: string; + assignments?: string /** * * @type {string} * @memberof Subscription */ - remaining?: string; + remaining?: string /** * * @type {string} * @memberof Subscription */ - available?: string; + available?: string } /** @@ -657,25 +658,25 @@ export interface Test { * @type {string} * @memberof Test */ - pk?: string; + pk?: string /** * * @type {string} * @memberof Test */ - name: string; + name: string /** * * @type {string} * @memberof Test */ - label: string; + label: string /** * * @type {string} * @memberof Test */ - annotation: string; + annotation: string } /** @@ -689,37 +690,37 @@ export interface Tutor { * @type {string} * @memberof Tutor */ - pk?: string; + pk?: string /** * * @type {string} * @memberof Tutor */ - password?: string; + password?: string /** * Designates whether this user should be treated as active. Unselect this instead of deleting accounts. * @type {boolean} * @memberof Tutor */ - isActive?: boolean; + isActive?: boolean /** * Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only. * @type {string} * @memberof Tutor */ - username: string; + username: string /** * * @type {string} * @memberof Tutor */ - feedbackCreated?: string; + feedbackCreated?: string /** * * @type {string} * @memberof Tutor */ - feedbackValidated?: string; + feedbackValidated?: string } /** @@ -733,31 +734,31 @@ export interface UserAccount { * @type {string} * @memberof UserAccount */ - pk?: string; + pk?: string /** * Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only. * @type {string} * @memberof UserAccount */ - username?: string; + username?: string /** * * @type {string} * @memberof UserAccount */ - role?: UserAccount.RoleEnum; + role?: UserAccount.RoleEnum /** * * @type {boolean} * @memberof UserAccount */ - isAdmin?: boolean; + isAdmin?: boolean /** * * @type {string} * @memberof UserAccount */ - password?: string; + password?: string } /** @@ -787,41 +788,41 @@ export interface VisibleCommentFeedback { * @type {number} * @memberof VisibleCommentFeedback */ - pk?: number; + pk?: number /** * * @type {string} * @memberof VisibleCommentFeedback */ - ofSubmission: string; + ofSubmission: string /** * * @type {boolean} * @memberof VisibleCommentFeedback */ - isFinal?: boolean; + isFinal?: boolean /** * * @type {number} * @memberof VisibleCommentFeedback */ - score?: number; + score?: number /** * * @type {string} * @memberof VisibleCommentFeedback */ - feedbackLines?: string; + feedbackLines?: string /** * * @type {Date} * @memberof VisibleCommentFeedback */ - created?: Date; + created?: Date /** * * @type {string} * @memberof VisibleCommentFeedback */ - ofSubmissionType?: string; + ofSubmissionType?: string } diff --git a/frontend/src/pages/student/StudentLayout.vue b/frontend/src/pages/student/StudentLayout.vue index 6701470b..f8bc7d5b 100644 --- a/frontend/src/pages/student/StudentLayout.vue +++ b/frontend/src/pages/student/StudentLayout.vue @@ -2,7 +2,7 @@ <base-layout> <template slot="header"> - {{ module_reference }} + {{ moduleReference }} </template> <v-list dense slot="sidebar-content"> @@ -60,7 +60,7 @@ export default { }, computed: { ...mapState({ - module_reference: state => state.studentPage.exam.module_reference, + moduleReference: state => state.studentPage.exam.moduleReference, submissions: state => state.studentPage.submissionsForList, exam: state => state.studentPage.exam, visited: state => state.studentPage.visited, diff --git a/frontend/src/pages/student/StudentSubmissionPage.vue b/frontend/src/pages/student/StudentSubmissionPage.vue index a0c3aea7..e6928c52 100644 --- a/frontend/src/pages/student/StudentSubmissionPage.vue +++ b/frontend/src/pages/student/StudentSubmissionPage.vue @@ -17,15 +17,15 @@ <v-spacer/> - <h2>Score: {{feedback ? feedback.score : 'N/A'}} / {{submissionType.full_score}}</h2> + <h2>Score: {{feedback ? feedback.score : 'N/A'}} / {{submissionType.fullScore}}</h2> </v-toolbar> <template slot="table-content"> <tr v-for="(code, lineNo) in submission" :key="lineNo"> <submission-line :code="code" :lineNo="lineNo"> <template v-if="feedback"> - <template v-for="(comment, index) in feedback.feedback_lines[lineNo]"> + <template v-for="(comment, index) in feedback.feedbackLines[lineNo]"> <feedback-comment - v-if="feedback.feedback_lines[lineNo] && showFeedback" + v-if="feedback.feedbackLines[lineNo] && showFeedback" v-bind="comment" :line-no="lineNo" :key="index" diff --git a/frontend/src/store/getters.ts b/frontend/src/store/getters.ts index 3cdc0cbe..12504862 100644 --- a/frontend/src/store/getters.ts +++ b/frontend/src/store/getters.ts @@ -1,7 +1,7 @@ const getters = { corrected (state) { - return state.statistics.submission_type_progress.every(progress => { - return progress.feedback_final === progress.submission_count + return state.statistics.submissionTypeProgress.every(progress => { + return progress.feedbackFinal === progress.submissionCount }) }, getSubmission: state => pk => { diff --git a/frontend/src/store/modules/authentication.ts b/frontend/src/store/modules/authentication.ts index 9e0c95e7..c3a81187 100644 --- a/frontend/src/store/modules/authentication.ts +++ b/frontend/src/store/modules/authentication.ts @@ -17,7 +17,7 @@ interface AuthState { pk: string, username: string, role: string, - is_admin: boolean //eslint-disable-line + isAdmin: boolean } } function initialState (): AuthState { @@ -31,7 +31,7 @@ function initialState (): AuthState { pk: '', username: '', role: '', - is_admin: false + isAdmin: false } } } diff --git a/frontend/src/store/modules/feedback_list/feedback-table.ts b/frontend/src/store/modules/feedback_list/feedback-table.ts index 4ea766a8..845fa13d 100644 --- a/frontend/src/store/modules/feedback_list/feedback-table.ts +++ b/frontend/src/store/modules/feedback_list/feedback-table.ts @@ -19,7 +19,7 @@ const feedbackTable = { state: initialState(), mutations: { [feedbackTableMut.SET_FEEDBACK_HISTORY]: (state, val) => { - state.feedbackHist = objectifyArray(val, 'of_submission') + state.feedbackHist = objectifyArray(val, 'ofSubmission') }, [feedbackTableMut.ADD_ASSIGNMENTS_INFO]: (state, assignments) => { for (const assignment of assignments) { @@ -28,20 +28,20 @@ const feedbackTable = { ...feedback.history, [assignment.stage]: { owner: assignment.owner, - is_done: assignment.is_done + isDone: assignment.isDone } } } }, [feedbackTableMut.SET_FEEDBACK_OF_SUBMISSION_TYPE] (state, {feedback, type}) { - state.feedbackHist[feedback.of_submission].of_submission_type = type.name + state.feedbackHist[feedback.ofSubmission].ofSubmissionType = type.name }, [feedbackTableMut.RESET_STATE]: (state) => { Object.assign(state, initialState()) } }, actions: { mapFeedbackHistOfSubmissionType ({getters, commit, state}) { for (const feedback of Object.values(state.feedbackHist)) { - const type = getters.getSubmissionType((feedback as any).of_submission_type) + const type = getters.getSubmissionType((feedback as any).ofSubmissionType) commit(feedbackTableMut.SET_FEEDBACK_OF_SUBMISSION_TYPE, {feedback, type}) } }, diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts index cd02557f..f6619be2 100644 --- a/frontend/src/store/modules/submission-notes.ts +++ b/frontend/src/store/modules/submission-notes.ts @@ -35,11 +35,11 @@ function initialState () { origFeedback: { score: null, isFinal: false, - feedback_lines: {} + feedbackLines: {} }, updatedFeedback: { score: null, - feedback_lines: {} + feedbackLines: {} }, commentsMarkedForDeletion: {} } @@ -58,7 +58,7 @@ const submissionNotes = { // this makes iterating over the submission much more pleasant submission: (state, getters) => { const language = getters.submissionType - ? getters.submissionType.programming_language + ? getters.submissionType.programmingLanguage : 'c' const highlighted = hljs.highlight(language, state.submission.text, true).value return highlighted.split('\n').reduce((acc, cur, index) => { @@ -71,12 +71,12 @@ const submissionNotes = { }, workInProgress: state => { const openEditor = Object.values(state.ui.showEditorOnLine).reduce((acc, curr) => acc || curr, false) - const feedbackWritten = Object.entries(state.updatedFeedback.feedback_lines).length > 0 + const feedbackWritten = Object.entries(state.updatedFeedback.feedbackLines).length > 0 return openEditor || feedbackWritten }, isFeedbackCreation: state => { - return !state.origFeedback['feedback_stage_for_user'] || - state.origFeedback['feedback_stage_for_user'] === 'feedback-creation' + return !state.origFeedback['feedbackStageForUser'] || + state.origFeedback['feedbackStageForUser'] === 'feedback-creation' } }, mutations: { @@ -93,13 +93,13 @@ const submissionNotes = { state.ui.showFeedback = val }, [subNotesMut.UPDATE_FEEDBACK_LINE]: function (state, feedback) { - Vue.set(state.updatedFeedback.feedback_lines, feedback.lineNo, feedback.comment) + Vue.set(state.updatedFeedback.feedbackLines, feedback.lineNo, feedback.comment) }, [subNotesMut.UPDATE_FEEDBACK_SCORE]: function (state, score) { state.updatedFeedback.score = score }, [subNotesMut.DELETE_FEEDBACK_LINE]: function (state, lineNo) { - Vue.delete(state.updatedFeedback.feedback_lines, lineNo) + Vue.delete(state.updatedFeedback.feedbackLines, lineNo) }, [subNotesMut.TOGGLE_EDITOR_ON_LINE]: function (state, {lineNo, comment}) { Vue.set(state.ui.selectedCommentOnLine, lineNo, comment) @@ -128,11 +128,11 @@ const submissionNotes = { }, submitFeedback: async function ({state, dispatch, getters}, {isFinal = false}) { let feedback = { - is_final: isFinal, - of_submission: state.submission.pk + isFinal: isFinal, + ofSubmission: state.submission.pk } - if (Object.keys(state.updatedFeedback.feedback_lines).length > 0) { - feedback['feedback_lines'] = state.updatedFeedback.feedback_lines + if (Object.keys(state.updatedFeedback.feedbackLines).length > 0) { + feedback['feedbackLines'] = state.updatedFeedback.feedbackLines } if (state.origFeedback.score === null && state.updatedFeedback.score === null) { throw new Error('You need to give a score.') diff --git a/frontend/src/store/modules/subscriptions.ts b/frontend/src/store/modules/subscriptions.ts index 588867f0..79d7a1f8 100644 --- a/frontend/src/store/modules/subscriptions.ts +++ b/frontend/src/store/modules/subscriptions.ts @@ -1,6 +1,7 @@ import Vue from 'vue' import * as api from '@/api' -import {handleError, flatten, cartesian, once} from '@/util/helpers' +import {cartesian, flatten, handleError, once} from '@/util/helpers' +import {Subscription} from "@/models"; export const subscriptionMuts = Object.freeze({ SET_SUBSCRIPTIONS: 'SET_SUBSCRIPTIONS', @@ -59,17 +60,17 @@ const subscriptions = { return state.subscriptions[state.activeSubscriptionPk] }, resolveSubscriptionKeyToName: (state, getters, rootState) => subscription => { - switch (subscription.query_type) { + switch (subscription.queryType) { case 'random': return 'Active' case 'exam': - const examType = rootState.examTypes[subscription.query_key] - return examType ? examType.module_reference : 'Exam' + const examType = rootState.examTypes[subscription.queryKey] + return examType ? examType.moduleReference : 'Exam' case 'submission_type': - const submissionType = rootState.submissionTypes[subscription.query_key] + const submissionType = rootState.submissionTypes[subscription.queryKey] return submissionType ? submissionType.name : 'Submission Type' case 'student': - const studentName = rootState.students[subscription.query_key] + const studentName = rootState.students[subscription.queryKey] return studentName ? studentName.name : 'Student' } }, @@ -87,12 +88,12 @@ const subscriptions = { return acc }, {}) Object.values(state.subscriptions).forEach((subscription: any) => { - subscriptionsByStage[subscription.feedback_stage][subscription.query_type].push(subscription) + subscriptionsByStage[subscription.feedbackStage][subscription.queryType].push(subscription) }) // sort the resulting arrays in subscriptions lexicographically by their query_keys const sortSubscriptions = subscriptionsByType => Object.values(subscriptionsByType) .forEach((arr: object[]) => { - if (arr.length > 1 && arr[0].hasOwnProperty('query_key')) { + if (arr.length > 1 && arr[0].hasOwnProperty('queryKey')) { arr.sort((subA, subB) => { const subALower = getters.resolveSubscriptionKeyToName(subA).toLowerCase() const subBLower = getters.resolveSubscriptionKeyToName(subB).toLowerCase() @@ -146,7 +147,7 @@ const subscriptions = { if (type === 'random') { return true } - return elem.query_key === key + return elem.queryKey === key }) subscription = subscription || await api.subscribeTo(type, key, stage) commit(subscriptionMuts.SET_SUBSCRIPTION, subscription) @@ -168,7 +169,8 @@ const subscriptions = { }, /** * Creates as many assignments as needed to reach MAX_NUMBER_OF_ASSIGNMENTS - * @param numOfAssignments Use to override default behaviour of creating MAX_NUMBER_OF_ASSIGNMENTS - assignmentQueue.length assignments + * @param numOfAssignments Use to override default behaviour of + * creating MAX_NUMBER_OF_ASSIGNMENTS - assignmentQueue.length assignments * @returns {Promise<[Promise]>} returns Promise of Array of Promises of assignments */ async getAssignmentsForActiveSubscription ({commit, state, dispatch, getters}, numOfAssignments) { @@ -241,13 +243,13 @@ const subscriptions = { commit(subscriptionMuts.SET_ASSIGNMENT_QUEUE, []) commit(subscriptionMuts.SET_ACTIVE_SUBSCRIPTION_PK, '') }, - async subscribeToType ({commit, state, dispatch, getters}, type) { + async subscribeToType ({commit, state, dispatch, getters}, type: Subscription.QueryTypeEnum) { switch (type) { - case 'random': + case Subscription.QueryTypeEnum.Random: return getters.availableStages.map(stage => { dispatch('subscribeTo', {type, stage}) }) - case 'exam': + case Subscription.QueryTypeEnum.Exam: if (getters.isReviewer) { const stageKeyCartesian = cartesian( getters.availableStages, getters.availableExamTypeQueryKeys) @@ -256,7 +258,7 @@ const subscriptions = { }) } return [] - case 'submission_type': + case Subscription.QueryTypeEnum.SubmissionType: const stageKeyCartesian = cartesian( getters.availableStages, getters.availableSubmissionTypeQueryKeys) return stageKeyCartesian.map(([stage, key]) => { diff --git a/frontend/src/store/mutations.ts b/frontend/src/store/mutations.ts index 1c9e80ad..96fff066 100644 --- a/frontend/src/store/mutations.ts +++ b/frontend/src/store/mutations.ts @@ -57,7 +57,7 @@ const mutations = { Vue.set(state.feedback, feedback.pk, { ...state.feedback[feedback.pk], ...feedback, - of_submission_type: state.submissionTypes[feedback['of_submission_type']] + ofSubmissionType: state.submissionTypes[feedback['ofSubmissionType']] }) }, [mut.SET_STATISTICS] (state, statistics) { @@ -68,9 +68,9 @@ const mutations = { }, [mut.MAP_FEEDBACK_OF_SUBMISSION_TYPE] (state) { Object.values(state.feedback).forEach(feedback => { - const submissionType = state.submissionTypes[feedback['of_submission_type']] + const submissionType = state.submissionTypes[feedback['ofSubmissionType']] if (submissionType) { - feedback['of_submission_type'] = submissionType + feedback['ofSubmissionType'] = submissionType } }) }, @@ -96,7 +96,7 @@ function mapStudent (student, map) { if (Object.keys(map).length > 0) { return { ...student, - ...map[student.matrikel_no] + ...map[student.matrikelNo] } } return student diff --git a/frontend/src/store/store.ts b/frontend/src/store/store.ts index 9b00fccb..db8c0a28 100644 --- a/frontend/src/store/store.ts +++ b/frontend/src/store/store.ts @@ -17,6 +17,7 @@ import {lastInteraction} from '@/store/plugins/lastInteractionPlugin' Vue.use(Vuex) + export function initialState () { return { lastAppInteraction: Date.now(), @@ -27,7 +28,7 @@ export function initialState () { students: {}, studentMap: {}, // is used to map obfuscated student data back to the original statistics: { - submission_type_progress: [] + submissionTypeProgress: [] }, tutors: [] } -- GitLab From b95340f98bb423fcf019652c3df69469fd652e83 Mon Sep 17 00:00:00 2001 From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de> Date: Mon, 13 Aug 2018 00:01:40 +0200 Subject: [PATCH 4/9] Soo so many added typings... Nearly completed typing the whole store! --- core/urls.py | 3 +- frontend/package.json | 1 + frontend/src/api.ts | 20 +- .../submission_notes/SubmissionCorrection.vue | 1 - frontend/src/models.ts | 48 +- frontend/src/store/actions.ts | 15 +- frontend/src/store/getters.ts | 12 +- frontend/src/store/modules/authentication.ts | 15 +- .../feedback_list/feedback-search-options.ts | 18 +- .../modules/feedback_list/feedback-table.ts | 50 +- frontend/src/store/modules/student-page.ts | 25 +- .../src/store/modules/submission-notes.ts | 68 +- frontend/src/store/modules/subscriptions.ts | 89 ++- frontend/src/store/modules/ui.ts | 21 +- frontend/src/store/mutations.ts | 53 +- .../store/plugins/lastInteractionPlugin.ts | 5 +- frontend/src/store/store.ts | 23 +- frontend/src/util/helpers.ts | 34 +- frontend/yarn.lock | 691 +----------------- grady/settings/default.py | 9 +- requirements.txt | 2 + 21 files changed, 325 insertions(+), 878 deletions(-) diff --git a/core/urls.py b/core/urls.py index 3e3014d9..a042f5f9 100644 --- a/core/urls.py +++ b/core/urls.py @@ -2,10 +2,9 @@ from django.urls import path, re_path from drf_yasg.views import get_schema_view from drf_yasg import openapi from rest_framework.routers import DefaultRouter -from rest_framework.permissions import IsAdminUser, AllowAny +from rest_framework.permissions import AllowAny from core import views -from core.permissions import IsReviewer # Create a router and register our viewsets with it. router = DefaultRouter() diff --git a/frontend/package.json b/frontend/package.json index ab0d228b..3eca1380 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -26,6 +26,7 @@ }, "devDependencies": { "@types/chai": "^4.1.0", + "@types/highlight.js": "^9.12.3", "@types/mocha": "^5.2.4", "@vue/cli-plugin-e2e-nightwatch": "^3.0.0-rc.10", "@vue/cli-plugin-eslint": "^3.0.0-rc.10", diff --git a/frontend/src/api.ts b/frontend/src/api.ts index a50422e5..1f2d2c17 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -1,4 +1,4 @@ -import axios, {AxiosInstance, AxiosPromise, AxiosResponse} from 'axios' +import axios, {AxiosInstance, AxiosResponse} from 'axios' import {Credentials} from '@/store/modules/authentication' import { Assignment, @@ -46,7 +46,7 @@ export async function fetchJWT (credentials: Credentials): Promise<JSONWebToken> } export async function refreshJWT (oldToken: string): Promise<JSONWebToken> { - const token: string = (await ax.post('/api/refresh-oldToken/', {oldToken})).data.token + const token: string = (await ax.post('/api/refresh-token/', {oldToken})).data.token ax.defaults.headers['Authorization'] = `JWT ${token}` return {token} } @@ -134,10 +134,16 @@ export async function fetchStatistics (opt = {fields: []}): Promise<Statistics> return (await ax.get(url)).data } +interface SubscriptionCreatePayload { + queryType: Subscription.QueryTypeEnum + queryKey?: string + feedbackStage?: Subscription.FeedbackStageEnum +} + export async function subscribeTo (type: Subscription.QueryTypeEnum, - key: string, - stage: Subscription.FeedbackStageEnum): Promise<Subscription> { - let data: Subscription = { + key?: string, + stage?: Subscription.FeedbackStageEnum): Promise<Subscription> { + let data: SubscriptionCreatePayload = { queryType: type } @@ -160,7 +166,7 @@ export async function createAssignment ( return (await ax.post(`/api/assignment/`, data)).data } -export async function submitFeedbackForAssignment ({feedback}: {feedback: Feedback}): Promise<Feedback> { +export async function submitFeedbackForAssignment ({feedback}: {feedback: Partial<Feedback>}): Promise<Feedback> { return (await ax.post('/api/feedback/', feedback)).data } @@ -168,7 +174,7 @@ export async function submitUpdatedFeedback ({feedback}: {feedback: Feedback}): return (await ax.patch(`/api/feedback/${feedback.ofSubmission}/`, feedback)).data } -export async function fetchSubmissionTypes (fields = []): Promise<Array<SubmissionType>> { +export async function fetchSubmissionTypes (fields: Array<string> = []): Promise<Array<SubmissionType>> { let url = '/api/submissiontype/' if (fields.length > 0) { url += '?fields=pk,' + fields diff --git a/frontend/src/components/submission_notes/SubmissionCorrection.vue b/frontend/src/components/submission_notes/SubmissionCorrection.vue index 3c70cffe..d7b5c17f 100644 --- a/frontend/src/components/submission_notes/SubmissionCorrection.vue +++ b/frontend/src/components/submission_notes/SubmissionCorrection.vue @@ -108,7 +108,6 @@ export default { 'isTutor', 'isReviewer', 'getSubmission', - 'getFeedback', 'getSubmissionType', 'workInProgress' ]), diff --git a/frontend/src/models.ts b/frontend/src/models.ts index 87153cb7..c5f59b16 100644 --- a/frontend/src/models.ts +++ b/frontend/src/models.ts @@ -9,7 +9,7 @@ export interface Assignment { * @type {string} * @memberof Assignment */ - pk?: string + pk: string /** * * @type {string} @@ -33,7 +33,11 @@ export interface Assignment { * @type {string} * @memberof Assignment */ - stage?: string + stage?: Subscription.FeedbackStageEnum + + feedback?: Feedback + + subscription?: string } /** @@ -47,7 +51,7 @@ export interface Exam { * @type {string} * @memberof Exam */ - pk?: string + pk: string /** * * @type {string} @@ -85,13 +89,13 @@ export interface Feedback { * @type {number} * @memberof Feedback */ - pk?: number + pk: number /** * * @type {string} * @memberof Feedback */ - ofSubmission: string + ofSubmission?: string /** * * @type {boolean} @@ -141,7 +145,7 @@ export interface FeedbackComment { * @type {string} * @memberof FeedbackComment */ - pk?: string + pk: string /** * * @type {string} @@ -217,7 +221,7 @@ export interface StudentInfo { * @type {string} * @memberof StudentInfo */ - pk?: string + pk: string /** * * @type {string} @@ -267,7 +271,7 @@ export interface StudentInfoForListView { * @type {string} * @memberof StudentInfoForListView */ - pk?: string + pk: string /** * * @type {string} @@ -329,7 +333,7 @@ export interface Submission { * @type {string} * @memberof Submission */ - pk?: string + pk: string /** * * @type {SubmissionType} @@ -367,7 +371,7 @@ export interface SubmissionList { * @type {string} * @memberof SubmissionList */ - pk?: string + pk: string /** * * @type {SubmissionTypeList} @@ -393,7 +397,7 @@ export interface SubmissionNoTextFields { * @type {string} * @memberof SubmissionNoTextFields */ - pk?: string + pk: string /** * * @type {string} @@ -431,7 +435,7 @@ export interface SubmissionNoType { * @type {string} * @memberof SubmissionNoType */ - pk?: string + pk: string /** * * @type {string} @@ -455,7 +459,7 @@ export interface SubmissionNoType { * @type {Feedback} * @memberof SubmissionNoType */ - feedback: Feedback + feedback?: Feedback /** * * @type {Array<Test>} @@ -475,7 +479,7 @@ export interface SubmissionType { * @type {string} * @memberof SubmissionType */ - pk?: string + pk: string /** * * @type {string} @@ -534,7 +538,7 @@ export interface SubmissionTypeList { * @type {string} * @memberof SubmissionTypeList */ - pk?: string + pk: string /** * @@ -551,7 +555,7 @@ export interface SubmissionTypeList { } export interface SubmissionTypeProgress { - pk?: string + pk: string name: string feedbackFinal: number feedbackInValidation: number @@ -570,7 +574,7 @@ export interface Subscription { * @type {string} * @memberof Subscription */ - pk?: string + pk: string /** * * @type {string} @@ -606,7 +610,7 @@ export interface Subscription { * @type {string} * @memberof Subscription */ - assignments?: string + assignments?: Array<Assignment> /** * * @type {string} @@ -658,7 +662,7 @@ export interface Test { * @type {string} * @memberof Test */ - pk?: string + pk: string /** * * @type {string} @@ -690,7 +694,7 @@ export interface Tutor { * @type {string} * @memberof Tutor */ - pk?: string + pk: string /** * * @type {string} @@ -734,7 +738,7 @@ export interface UserAccount { * @type {string} * @memberof UserAccount */ - pk?: string + pk: string /** * Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only. * @type {string} @@ -788,7 +792,7 @@ export interface VisibleCommentFeedback { * @type {number} * @memberof VisibleCommentFeedback */ - pk?: number + pk: number /** * * @type {string} diff --git a/frontend/src/store/actions.ts b/frontend/src/store/actions.ts index 47ca46fc..24babbe0 100644 --- a/frontend/src/store/actions.ts +++ b/frontend/src/store/actions.ts @@ -4,8 +4,10 @@ import { subNotesMut } from '@/store/modules/submission-notes' import * as api from '@/api' import router from '@/router/index' import { handleError } from '@/util/helpers' +import {ActionTree} from 'vuex' +import {RootState} from '@/store/store' -const actions = { +const actions: ActionTree<RootState, RootState> = { async getExamTypes ({commit, dispatch}) { try { const examTypes = await api.fetchExamType({}) @@ -14,7 +16,7 @@ const actions = { handleError(err, dispatch, 'Unable to fetch exam types') } }, - async updateSubmissionTypes ({commit, dispatch}, fields = []) { + async updateSubmissionTypes ({commit, dispatch}, fields: Array<string> = []) { try { const submissionTypes = await api.fetchSubmissionTypes(fields) submissionTypes.forEach(type => { @@ -24,7 +26,9 @@ const actions = { handleError(err, dispatch, 'Unable to get submission types') } }, - async getStudents ({commit, dispatch}, opt = {studentPks: [], fields: []}) { + async getStudents ({commit, dispatch}, + opt: {studentPks: Array<string>, fields: Array<string>} = + {studentPks: [], fields: []}) { try { if (opt.studentPks.length === 0) { const students = await api.fetchAllStudents() @@ -32,7 +36,7 @@ const actions = { return students } else { const students = await Promise.all( - opt.studentPks.map(pk => api.fetchStudent({ + opt.studentPks.map((pk: string) => api.fetchStudent({ pk, fields: opt.fields })) @@ -104,7 +108,8 @@ const actions = { logout ({ commit, getters, state }, message = '') { if (getters.isStudent) { // change active to false when user logs out - api.changeActiveForUser(state.authentication.user.pk, false) + // TODO this should belong in auth module + api.changeActiveForUser((state as any).authentication.user.pk, false) } commit('RESET_STATE') commit('submissionNotes/' + subNotesMut.RESET_STATE) diff --git a/frontend/src/store/getters.ts b/frontend/src/store/getters.ts index 12504862..44e8a56d 100644 --- a/frontend/src/store/getters.ts +++ b/frontend/src/store/getters.ts @@ -1,16 +1,16 @@ -const getters = { +import {RootState} from '@/store/store' +import {GetterTree} from 'vuex' + +const getters: GetterTree<RootState, RootState> = { corrected (state) { return state.statistics.submissionTypeProgress.every(progress => { return progress.feedbackFinal === progress.submissionCount }) }, - getSubmission: state => pk => { + getSubmission: state => (pk: string) => { return state.submissions[pk] }, - getFeedback: state => pk => { - return state.feedback[pk] - }, - getSubmissionType: state => pk => { + getSubmissionType: state => (pk: string) => { return state.submissionTypes[pk] } } diff --git a/frontend/src/store/modules/authentication.ts b/frontend/src/store/modules/authentication.ts index c3a81187..1b8381f5 100644 --- a/frontend/src/store/modules/authentication.ts +++ b/frontend/src/store/modules/authentication.ts @@ -1,6 +1,7 @@ import * as api from '@/api' import gradySays from '../grady_speak' import {ActionContext, Module} from 'vuex' +import {UserAccount} from '@/models' export interface Credentials { username: string, @@ -13,12 +14,7 @@ interface AuthState { refreshingToken: boolean, jwtTimeDelta: number, message: string, - user: { - pk: string, - username: string, - role: string, - isAdmin: boolean - } + user: UserAccount } function initialState (): AuthState { return { @@ -30,7 +26,6 @@ function initialState (): AuthState { user: { pk: '', username: '', - role: '', isAdmin: false } } @@ -53,13 +48,13 @@ const authentication: Module<AuthState, any> = { return gradySays[Math.floor(Math.random() * gradySays.length)] }, isStudent: (state: AuthState) => { - return state.user.role === 'Student' + return state.user.role === UserAccount.RoleEnum.Student }, isTutor: (state: AuthState) => { - return state.user.role === 'Tutor' + return state.user.role === UserAccount.RoleEnum.Tutor }, isReviewer: (state: AuthState) => { - return state.user.role === 'Reviewer' + return state.user.role === UserAccount.RoleEnum.Reviewer }, isTutorOrReviewer: (state: AuthState, getters) => { return getters.isTutor || getters.isReviewer diff --git a/frontend/src/store/modules/feedback_list/feedback-search-options.ts b/frontend/src/store/modules/feedback_list/feedback-search-options.ts index c79120eb..4b89087f 100644 --- a/frontend/src/store/modules/feedback_list/feedback-search-options.ts +++ b/frontend/src/store/modules/feedback_list/feedback-search-options.ts @@ -1,3 +1,6 @@ +import {Module} from 'vuex' +import {RootState} from '@/store/store' + export const namespace = 'feedbackSearchOptions' export const feedbackSearchOptsMut = Object.freeze({ @@ -9,7 +12,16 @@ export const feedbackSearchOptsMut = Object.freeze({ SET_FILTER_BY_STAGE: 'SET_FILTER_BY_STAGE' }) -function initialState () { +interface FeedbackSearchOptionsState { + showFinal: boolean + searchOtherUserComments: boolean + caseSensitive: boolean + useRegex: boolean + filterByTutors: string[] + filterByStage: string +} + +function initialState (): FeedbackSearchOptionsState { return { showFinal: false, searchOtherUserComments: false, @@ -20,12 +32,12 @@ function initialState () { } } -const mapStageDisplayToApiString = { +const mapStageDisplayToApiString: {[index: string]: string} = { 'Initial feedback': 'feedback-creation', 'Validation': 'feedback-validation' } -const feedbackSearchOptions = { +const feedbackSearchOptions: Module<FeedbackSearchOptionsState, RootState> = { namespaced: true, state: initialState(), getters: { diff --git a/frontend/src/store/modules/feedback_list/feedback-table.ts b/frontend/src/store/modules/feedback_list/feedback-table.ts index 845fa13d..f005582e 100644 --- a/frontend/src/store/modules/feedback_list/feedback-table.ts +++ b/frontend/src/store/modules/feedback_list/feedback-table.ts @@ -1,5 +1,8 @@ import {fetchAllFeedback, fetchAllAssignments} from '@/api' import {objectifyArray} from '@/util/helpers' +import {Assignment, Feedback, Subscription} from '@/models' +import {Module} from 'vuex' +import {RootState} from '@/store/store' export const feedbackTableMut = Object.freeze({ SET_FEEDBACK_HISTORY: 'SET_FEEDBACK_HISTORY', @@ -9,20 +12,36 @@ export const feedbackTableMut = Object.freeze({ RESET_STATE: 'RESET_STATE' }) -function initialState () { +export interface FeedbackHistoryItem extends Feedback { + history?: { + [key in Subscription.FeedbackStageEnum]: { + owner: string + isDone: boolean + } + } +} + +interface FeedbackTableState { + feedbackHist: {[submissionPk: string]: FeedbackHistoryItem} +} + +function initialState (): FeedbackTableState { return { feedbackHist: {} } } -const feedbackTable = { +const feedbackTable: Module<FeedbackTableState, RootState> = { state: initialState(), mutations: { - [feedbackTableMut.SET_FEEDBACK_HISTORY]: (state, val) => { + [feedbackTableMut.SET_FEEDBACK_HISTORY]: (state, val: Array<Feedback>) => { state.feedbackHist = objectifyArray(val, 'ofSubmission') }, - [feedbackTableMut.ADD_ASSIGNMENTS_INFO]: (state, assignments) => { + [feedbackTableMut.ADD_ASSIGNMENTS_INFO]: (state, assignments: Array<Assignment>) => { for (const assignment of assignments) { + if (!assignment.submission || !assignment.stage) { + throw Error() + } const feedback = state.feedbackHist[assignment.submission] feedback.history = { ...feedback.history, @@ -46,18 +65,17 @@ const feedbackTable = { } }, async getFeedbackHistory ({getters, commit, dispatch}) { - let data = [] - data.push(fetchAllFeedback()) - if (getters.isReviewer) { - data.push(fetchAllAssignments()) - } - Promise.all(data).then(([feedback, assignments]) => { - commit(feedbackTableMut.SET_FEEDBACK_HISTORY, feedback) - dispatch('mapFeedbackHistOfSubmissionType') - if (assignments) { - commit(feedbackTableMut.ADD_ASSIGNMENTS_INFO, assignments) - } - }) + let data: [Promise<Feedback[]>, Promise<Assignment[]> | undefined] = + [fetchAllFeedback(), getters.isReviewer() ? fetchAllAssignments() : undefined] + + Promise.all<Feedback[], Assignment[] | undefined>(data) + .then(([feedbacks, assignments]: [Feedback[], Assignment[]?]) => { + commit(feedbackTableMut.SET_FEEDBACK_HISTORY, feedbacks) + dispatch('mapFeedbackHistOfSubmissionType') + if (assignments) { + commit(feedbackTableMut.ADD_ASSIGNMENTS_INFO, assignments) + } + }) } } } diff --git a/frontend/src/store/modules/student-page.ts b/frontend/src/store/modules/student-page.ts index f676cf4f..5125c7e4 100644 --- a/frontend/src/store/modules/student-page.ts +++ b/frontend/src/store/modules/student-page.ts @@ -1,9 +1,21 @@ import {fetchStudentSelfData, fetchStudentSubmissions} from '@/api' +import {Exam, Submission, SubmissionList} from '@/models' +import {RootState} from '@/store/store' +import {Module} from 'vuex' -function initialState () { +interface StudentPageState { + studentName: string + exam?: Exam + submissionsForList: Array<SubmissionList> + submissionData: {[typePk: string]: Submission} + visited: {[typePk: string]: boolean} + loaded: boolean +} + +function initialState (): StudentPageState { return { studentName: '', - exam: {}, + exam: undefined, submissionsForList: [], submissionData: {}, visited: {}, @@ -22,7 +34,7 @@ export const studentPageMut = Object.freeze({ RESET_STATE: 'RESET_STATE' }) -const studentPage = { +const studentPage: Module<StudentPageState, RootState> = { state: initialState(), mutations: { [studentPageMut.SET_STUDENT_NAME] (state, name) { @@ -31,9 +43,6 @@ const studentPage = { [studentPageMut.SET_EXAM] (state, exam) { state.exam = exam }, - [studentPageMut.SET_SUBMISSION_TYPES] (state, submissionTypes) { - state.submissionTypes = submissionTypes - }, [studentPageMut.SET_SUBMISSIONS_FOR_LIST] (state, submissions) { state.submissionsForList = submissions }, @@ -43,8 +52,8 @@ const studentPage = { * the former array elements. This is done to have direct access to the data * via the SubmissionType id. */ - [studentPageMut.SET_FULL_SUBMISSION_DATA] (state, submissionData) { - state.submissionData = submissionData.reduce((acc, cur, index) => { + [studentPageMut.SET_FULL_SUBMISSION_DATA] (state, submissionData: Array<Submission>) { + state.submissionData = submissionData.reduce((acc: {[pk: string]: Submission}, cur) => { acc[cur.type.pk] = cur return acc }, {}) diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts index f6619be2..32a74341 100644 --- a/frontend/src/store/modules/submission-notes.ts +++ b/frontend/src/store/modules/submission-notes.ts @@ -1,7 +1,10 @@ import Vue from 'vue' -import hljs from 'highlight.js' +import * as hljs from 'highlight.js' import * as api from '@/api' import {nameSpacer} from '@/util/helpers' +import {Feedback, FeedbackComment, Submission, SubmissionNoType} from '@/models' +import {RootState} from '@/store/store' +import {Module} from 'vuex' export const subNotesNamespace = nameSpacer('submissionNotes/') @@ -19,12 +22,26 @@ export const subNotesMut = Object.freeze({ RESET_STATE: 'RESET_STATE' }) -function initialState () { +interface SubmissionNotesState { + submission: SubmissionNoType + ui: { + showEditorOnLine: {[lineNo: number]: boolean} + selectedCommentOnLine: {[lineNo: number]: FeedbackComment} + showFeedback: boolean + }, + hasOrigFeedback: boolean + origFeedback: Feedback + updatedFeedback: Feedback + commentsMarkedForDeletion: {[pk: string]: FeedbackComment} +} + +function initialState (): SubmissionNotesState { return { - assignment: '', submission: { text: '', - pk: '' + pk: '', + type: '', + tests: [] }, ui: { showEditorOnLine: {}, @@ -33,19 +50,21 @@ function initialState () { }, hasOrigFeedback: false, origFeedback: { - score: null, + pk: 0, + score: 0, isFinal: false, feedbackLines: {} }, updatedFeedback: { - score: null, + pk: 0, + score: 0, feedbackLines: {} }, commentsMarkedForDeletion: {} } } -const submissionNotes = { +const submissionNotes: Module<SubmissionNotesState, RootState> = { namespaced: true, state: initialState(), getters: { @@ -60,8 +79,8 @@ const submissionNotes = { const language = getters.submissionType ? getters.submissionType.programmingLanguage : 'c' - const highlighted = hljs.highlight(language, state.submission.text, true).value - return highlighted.split('\n').reduce((acc, cur, index) => { + const highlighted = hljs.highlight(language, state.submission.text || '', true).value + return highlighted.split('\n').reduce((acc: {[k: number]: string}, cur, index) => { acc[index + 1] = cur return acc }, {}) @@ -71,7 +90,7 @@ const submissionNotes = { }, workInProgress: state => { const openEditor = Object.values(state.ui.showEditorOnLine).reduce((acc, curr) => acc || curr, false) - const feedbackWritten = Object.entries(state.updatedFeedback.feedbackLines).length > 0 + const feedbackWritten = Object.entries(state.updatedFeedback.feedbackLines || {}).length > 0 return openEditor || feedbackWritten }, isFeedbackCreation: state => { @@ -80,26 +99,31 @@ const submissionNotes = { } }, mutations: { - [subNotesMut.SET_SUBMISSION]: function (state, submission) { + [subNotesMut.SET_SUBMISSION]: function (state, submission: SubmissionNoType) { state.submission = submission }, - [subNotesMut.SET_ORIG_FEEDBACK]: function (state, feedback) { + [subNotesMut.SET_ORIG_FEEDBACK]: function (state, feedback: Feedback) { if (feedback) { state.origFeedback = feedback state.hasOrigFeedback = true } }, - [subNotesMut.SET_SHOW_FEEDBACK]: function (state, val) { + [subNotesMut.SET_SHOW_FEEDBACK]: function (state, val: boolean) { state.ui.showFeedback = val }, - [subNotesMut.UPDATE_FEEDBACK_LINE]: function (state, feedback) { - Vue.set(state.updatedFeedback.feedbackLines, feedback.lineNo, feedback.comment) + [subNotesMut.UPDATE_FEEDBACK_LINE]: function (state, feedback: {lineNo: number, comment: Partial<FeedbackComment>}) { + // explicit .toString() on lineNo is necessary because of Vue.set typings + if (state.updatedFeedback.feedbackLines) { + Vue.set(state.updatedFeedback.feedbackLines, feedback.lineNo.toString(), feedback.comment) + } }, [subNotesMut.UPDATE_FEEDBACK_SCORE]: function (state, score) { state.updatedFeedback.score = score }, [subNotesMut.DELETE_FEEDBACK_LINE]: function (state, lineNo) { - Vue.delete(state.updatedFeedback.feedbackLines, lineNo) + if (state.updatedFeedback.feedbackLines) { + Vue.delete(state.updatedFeedback.feedbackLines, lineNo) + } }, [subNotesMut.TOGGLE_EDITOR_ON_LINE]: function (state, {lineNo, comment}) { Vue.set(state.ui.selectedCommentOnLine, lineNo, comment) @@ -127,24 +151,24 @@ const submissionNotes = { ) }, submitFeedback: async function ({state, dispatch, getters}, {isFinal = false}) { - let feedback = { + let feedback: Partial<Feedback> = { isFinal: isFinal, ofSubmission: state.submission.pk } - if (Object.keys(state.updatedFeedback.feedbackLines).length > 0) { - feedback['feedbackLines'] = state.updatedFeedback.feedbackLines + if (Object.keys(state.updatedFeedback.feedbackLines || {}).length > 0) { + feedback.feedbackLines = state.updatedFeedback.feedbackLines } if (state.origFeedback.score === null && state.updatedFeedback.score === null) { throw new Error('You need to give a score.') } else if (state.updatedFeedback.score !== null) { - feedback['score'] = state.updatedFeedback.score + feedback.score = state.updatedFeedback.score } await dispatch('deleteComments') if (!state.hasOrigFeedback) { return api.submitFeedbackForAssignment({feedback}) } else { - feedback['pk']= state.origFeedback.pk - return api.submitUpdatedFeedback({feedback}) + feedback.pk = state.origFeedback.pk + return api.submitUpdatedFeedback(<{feedback: Feedback}> {feedback}) } } } diff --git a/frontend/src/store/modules/subscriptions.ts b/frontend/src/store/modules/subscriptions.ts index 79d7a1f8..dab0467f 100644 --- a/frontend/src/store/modules/subscriptions.ts +++ b/frontend/src/store/modules/subscriptions.ts @@ -1,7 +1,9 @@ import Vue from 'vue' import * as api from '@/api' import {cartesian, flatten, handleError, once} from '@/util/helpers' -import {Subscription} from "@/models"; +import {Assignment, Subscription} from '@/models' +import {ActionContext, Module} from 'vuex' +import {RootState} from '@/store/store' export const subscriptionMuts = Object.freeze({ SET_SUBSCRIPTIONS: 'SET_SUBSCRIPTIONS', @@ -14,7 +16,14 @@ export const subscriptionMuts = Object.freeze({ RESET_STATE: 'RESET_STATE' }) -function initialState () { +interface SubscriptionsState { + subscriptions: {[pk: string]: Subscription} + assignmentQueue: Array<Assignment> + activeSubscriptionPk: string + loading: boolean +} + +function initialState (): SubscriptionsState { return { subscriptions: {}, assignmentQueue: [], @@ -26,7 +35,7 @@ function initialState () { const MAX_NUMBER_OF_ASSIGNMENTS = 2 // noinspection JSCommentMatchesSignature -const subscriptions = { +const subscriptions: Module<SubscriptionsState, RootState> = { state: initialState(), getters: { availableTypes (state, getters) { @@ -59,21 +68,22 @@ const subscriptions = { activeSubscription (state) { return state.subscriptions[state.activeSubscriptionPk] }, - resolveSubscriptionKeyToName: (state, getters, rootState) => subscription => { + resolveSubscriptionKeyToName: (state, getters, rootState) => (subscription: Subscription) => { switch (subscription.queryType) { - case 'random': + case Subscription.QueryTypeEnum.Random: return 'Active' - case 'exam': - const examType = rootState.examTypes[subscription.queryKey] - return examType ? examType.moduleReference : 'Exam' - case 'submission_type': - const submissionType = rootState.submissionTypes[subscription.queryKey] - return submissionType ? submissionType.name : 'Submission Type' - case 'student': - const studentName = rootState.students[subscription.queryKey] - return studentName ? studentName.name : 'Student' + case Subscription.QueryTypeEnum.Exam: + return subscription.queryKey + ? rootState.examTypes[subscription.queryKey].moduleReference : 'Exam' + case Subscription.QueryTypeEnum.SubmissionType: + return subscription.queryKey + ? rootState.submissionTypes[subscription.queryKey].name : 'Submission Type' + case Subscription.QueryTypeEnum.Student: + return subscription.queryKey + ? rootState.students[subscription.queryKey].name : 'Student' } }, + // TODO Refactor this monstrosity getSubscriptionsGroupedByType (state, getters) { const subscriptionsByType = () => { return { @@ -114,37 +124,40 @@ const subscriptions = { } }, mutations: { - [subscriptionMuts.SET_SUBSCRIPTIONS] (state, subscriptions) { - state.subscriptions = subscriptions.reduce((acc, curr) => { - acc[curr['pk']] = curr + [subscriptionMuts.SET_SUBSCRIPTIONS] (state, subscriptions: Array<Subscription>): void { + state.subscriptions = subscriptions.reduce((acc: {[pk: string]: Subscription}, curr) => { + acc[curr.pk] = curr return acc }, {}) }, - [subscriptionMuts.SET_SUBSCRIPTION] (state, subscription) { + [subscriptionMuts.SET_SUBSCRIPTION] (state, subscription: Subscription): void { Vue.set(state.subscriptions, subscription.pk, subscription) }, - [subscriptionMuts.SET_ACTIVE_SUBSCRIPTION_PK] (state, subscriptionPk) { + [subscriptionMuts.SET_ACTIVE_SUBSCRIPTION_PK] (state, subscriptionPk: string): void { state.activeSubscriptionPk = subscriptionPk }, - [subscriptionMuts.SET_ASSIGNMENT_QUEUE] (state, queue) { + [subscriptionMuts.SET_ASSIGNMENT_QUEUE] (state, queue: Array<Assignment>): void { state.assignmentQueue = queue }, - [subscriptionMuts.ADD_ASSIGNMENT_TO_QUEUE] (state, assignment) { + [subscriptionMuts.ADD_ASSIGNMENT_TO_QUEUE] (state, assignment: Assignment): void { state.assignmentQueue.push(assignment) }, - [subscriptionMuts.POP_ASSIGNMENT_FROM_QUEUE] (state) { + [subscriptionMuts.POP_ASSIGNMENT_FROM_QUEUE] (state): void { state.assignmentQueue.shift() }, - [subscriptionMuts.RESET_STATE] (state) { + [subscriptionMuts.RESET_STATE] (state): void { Object.assign(state, initialState()) } }, actions: { - subscribeTo: async function ({commit, dispatch, getters}, {type, key, stage}) { + subscribeTo: async function ( + {commit, dispatch, getters}, + {type, key, stage}: + {type: Subscription.QueryTypeEnum, key?: string, stage: Subscription.FeedbackStageEnum}) { try { // don't subscribe to type, key, stage combinations if they're already present - let subscription = getters.getSubscriptionsGroupedByType[stage][type].find(elem => { - if (type === 'random') { + let subscription = getters.getSubscriptionsGroupedByType[stage][type].find((elem: Subscription) => { + if (type === Subscription.QueryTypeEnum.Random) { return true } return elem.queryKey === key @@ -189,11 +202,13 @@ const subscriptions = { } }, async cleanAssignmentsFromSubscriptions ({commit, state, dispatch}, excludeActive = true) { - Object.values(state.subscriptions).forEach((subscription: any) => { + Object.values(state.subscriptions).forEach(subscription => { if (!excludeActive || subscription.pk !== state.activeSubscriptionPk) { - subscription.assignments.forEach(assignment => { - api.deleteAssignment({assignment}) - }) + if (subscription.assignments) { + subscription.assignments.forEach(assignment => { + api.deleteAssignment({assignment}) + }) + } } }) }, @@ -206,7 +221,7 @@ const subscriptions = { // which will result get incorrectly interpreted as a an ended subscription return dispatch('getAssignmentsForActiveSubscription', 1) }).then(([promise]) => { - promise.then(assignment => { + promise.then((assignment: Assignment) => { commit(subscriptionMuts.ADD_ASSIGNMENT_TO_QUEUE, assignment) commit(subscriptionMuts.POP_ASSIGNMENT_FROM_QUEUE) }) @@ -243,17 +258,18 @@ const subscriptions = { commit(subscriptionMuts.SET_ASSIGNMENT_QUEUE, []) commit(subscriptionMuts.SET_ACTIVE_SUBSCRIPTION_PK, '') }, + // TODO use enums here async subscribeToType ({commit, state, dispatch, getters}, type: Subscription.QueryTypeEnum) { switch (type) { case Subscription.QueryTypeEnum.Random: - return getters.availableStages.map(stage => { + return getters.availableStages.map((stage: string) => { dispatch('subscribeTo', {type, stage}) }) case Subscription.QueryTypeEnum.Exam: if (getters.isReviewer) { const stageKeyCartesian = cartesian( getters.availableStages, getters.availableExamTypeQueryKeys) - return stageKeyCartesian.map(([stage, key]) => { + return stageKeyCartesian.map(([stage, key]: [string, string]) => { dispatch('subscribeTo', {stage, type, key}) }) } @@ -261,13 +277,14 @@ const subscriptions = { case Subscription.QueryTypeEnum.SubmissionType: const stageKeyCartesian = cartesian( getters.availableStages, getters.availableSubmissionTypeQueryKeys) - return stageKeyCartesian.map(([stage, key]) => { + return stageKeyCartesian.map(([stage, key]: [string, string]) => { dispatch('subscribeTo', {stage, type, key}) }) } }, - subscribeToAll: once(async ({commit, state, dispatch, getters}) => { - return Promise.all(flatten(getters.availableTypes.map(type => { + subscribeToAll: once(async ({commit, state, dispatch, getters}: + ActionContext<SubscriptionsState, RootState>) => { + return Promise.all(flatten(getters.availableTypes.map((type: string) => { return dispatch('subscribeToType', type) }))) }) diff --git a/frontend/src/store/modules/ui.ts b/frontend/src/store/modules/ui.ts index 2cecc601..b1760cea 100644 --- a/frontend/src/store/modules/ui.ts +++ b/frontend/src/store/modules/ui.ts @@ -1,5 +1,14 @@ +import {Module} from 'vuex' +import {RootState} from '@/store/store' -function initialState () { +interface UiState { + sideBarCollapsed: boolean + darkMode: boolean + darkModeUnlocked: boolean + showJumbotron: boolean +} + +function initialState (): UiState { return { sideBarCollapsed: false, darkMode: false, @@ -15,19 +24,19 @@ export const uiMut = Object.freeze({ SET_SHOW_JUMBOTRON: 'SET_SHOW_JUMBOTRON' }) -const ui = { +const ui: Module<UiState, RootState> = { state: initialState(), mutations: { - [uiMut.SET_SIDEBAR_COLLAPSED] (state, collapsed) { + [uiMut.SET_SIDEBAR_COLLAPSED] (state, collapsed: boolean) { state.sideBarCollapsed = collapsed }, - [uiMut.SET_DARK_MODE] (state, val) { + [uiMut.SET_DARK_MODE] (state, val: boolean) { state.darkMode = val }, - [uiMut.SET_DARK_MODE_UNLOCKED] (state, val) { + [uiMut.SET_DARK_MODE_UNLOCKED] (state, val: boolean) { state.darkModeUnlocked = val }, - [uiMut.SET_SHOW_JUMBOTRON] (state, val) { + [uiMut.SET_SHOW_JUMBOTRON] (state, val: boolean) { state.showJumbotron = val } } diff --git a/frontend/src/store/mutations.ts b/frontend/src/store/mutations.ts index 96fff066..8c6f165e 100644 --- a/frontend/src/store/mutations.ts +++ b/frontend/src/store/mutations.ts @@ -1,6 +1,8 @@ import Vue from 'vue' -import {initialState} from '@/store/store' +import {initialState, RootState} from '@/store/store' +import {MutationTree} from 'vuex' +import {Exam, Statistics, StudentInfoForListView, SubmissionNoType, SubmissionType, Tutor} from '@/models' export const mut = Object.freeze({ SET_LAST_INTERACTION: 'SET_LAST_INTERACTION', @@ -19,20 +21,20 @@ export const mut = Object.freeze({ RESET_STATE: 'RESET_STATE' }) -const mutations = { - [mut.SET_EXAM_TYPES] (state, examTypes) { - state.examTypes = examTypes.reduce((acc, curr) => { +const mutations: MutationTree<RootState> = { + [mut.SET_EXAM_TYPES] (state, examTypes: Array<Exam>) { + state.examTypes = examTypes.reduce((acc: {[pk: string]: Exam}, curr) => { acc[curr.pk] = curr return acc }, {}) }, - [mut.SET_STUDENTS] (state, students) { - state.students = students.reduce((acc, curr) => { + [mut.SET_STUDENTS] (state, students: Array<StudentInfoForListView>) { + state.students = students.reduce((acc: {[pk: string]: StudentInfoForListView}, curr) => { acc[curr.pk] = mapStudent(curr, state.studentMap) return acc }, {}) }, - [mut.SET_STUDENT] (state, student) { + [mut.SET_STUDENT] (state, student: StudentInfoForListView) { Vue.set(state.students, student.pk, mapStudent({ ...state.students[student.pk], ...student @@ -41,49 +43,25 @@ const mutations = { [mut.SET_STUDENT_MAP] (state, map) { state.studentMap = map }, - [mut.SET_TUTORS] (state, tutors) { + [mut.SET_TUTORS] (state, tutors: Array<Tutor>) { state.tutors = tutors }, - [mut.SET_SUBMISSION] (state, submission) { + [mut.SET_SUBMISSION] (state, submission: SubmissionNoType) { Vue.set(state.submissions, submission.pk, submission) }, - [mut.SET_ALL_FEEDBACK] (state, feedbackList) { - state.feedback = feedbackList.reduce((acc, curr) => { - acc[curr.pk] = curr - return acc - }, {}) - }, - [mut.SET_FEEDBACK] (state, feedback) { - Vue.set(state.feedback, feedback.pk, { - ...state.feedback[feedback.pk], - ...feedback, - ofSubmissionType: state.submissionTypes[feedback['ofSubmissionType']] - }) - }, - [mut.SET_STATISTICS] (state, statistics) { + [mut.SET_STATISTICS] (state, statistics: Statistics) { state.statistics = { ...state.statistics, ...statistics } }, - [mut.MAP_FEEDBACK_OF_SUBMISSION_TYPE] (state) { - Object.values(state.feedback).forEach(feedback => { - const submissionType = state.submissionTypes[feedback['ofSubmissionType']] - if (submissionType) { - feedback['ofSubmissionType'] = submissionType - } - }) - }, - [mut.UPDATE_SUBMISSION_TYPE] (state, submissionType) { + [mut.UPDATE_SUBMISSION_TYPE] (state, submissionType: SubmissionType) { const updatedSubmissionType = { ...state.submissionTypes[submissionType.pk], ...submissionType } Vue.set(state.submissionTypes, submissionType.pk, updatedSubmissionType) }, - [mut.UPDATE_SUBMISSION] (state, {submissionPk, payload, key}) { - Vue.set(state.submissions[submissionPk], key, payload) - }, [mut.SET_LAST_INTERACTION] (state) { state.lastAppInteraction = Date.now() }, @@ -92,8 +70,11 @@ const mutations = { } } -function mapStudent (student, map) { +function mapStudent (student: StudentInfoForListView, map: any) { if (Object.keys(map).length > 0) { + if (!student.matrikelNo) { + throw Error('Student objects need matrikelNo key in order to apply mapping') + } return { ...student, ...map[student.matrikelNo] diff --git a/frontend/src/store/plugins/lastInteractionPlugin.ts b/frontend/src/store/plugins/lastInteractionPlugin.ts index 7d0659e8..7c64f2ba 100644 --- a/frontend/src/store/plugins/lastInteractionPlugin.ts +++ b/frontend/src/store/plugins/lastInteractionPlugin.ts @@ -1,7 +1,8 @@ import {mut} from '@/store/mutations' +import {MutationPayload, Store} from 'vuex' -export function lastInteraction (store) { - store.subscribe((mutation) => { +export function lastInteraction (store: Store<void>) { + store.subscribe((mutation: MutationPayload) => { if (mutation.type !== mut.SET_LAST_INTERACTION) { store.commit(mut.SET_LAST_INTERACTION) } diff --git a/frontend/src/store/store.ts b/frontend/src/store/store.ts index db8c0a28..6ab9578e 100644 --- a/frontend/src/store/store.ts +++ b/frontend/src/store/store.ts @@ -14,20 +14,39 @@ import actions from './actions' import getters from './getters' import mutations from '@/store/mutations' import {lastInteraction} from '@/store/plugins/lastInteractionPlugin' +import { + Exam, + Statistics, + StudentInfoForListView, + SubmissionNoType, + SubmissionType, Tutor +} from '@/models' Vue.use(Vuex) +export interface RootState { + lastAppInteraction: number + examTypes: {[pk: string]: Exam} + submissionTypes: {[pk: string]: SubmissionType} + submissions: {[pk: string]: SubmissionNoType} + students: {[pk: string]: StudentInfoForListView} + studentMap: {} // is used to map obfuscated student data back to the original + statistics: Statistics + tutors: Array<Tutor> +} -export function initialState () { +export function initialState (): RootState { return { lastAppInteraction: Date.now(), examTypes: {}, submissionTypes: {}, submissions: {}, - feedback: {}, students: {}, studentMap: {}, // is used to map obfuscated student data back to the original statistics: { + submissionsPerType: 0, + submissionsPerStudent: 0, + currentMeanScore: 0, submissionTypeProgress: [] }, tutors: [] diff --git a/frontend/src/util/helpers.ts b/frontend/src/util/helpers.ts index b1d6ff27..11f1d8fe 100644 --- a/frontend/src/util/helpers.ts +++ b/frontend/src/util/helpers.ts @@ -1,3 +1,5 @@ +import {AxiosError} from "axios"; +import {Dispatch} from "vuex"; export function nameSpacer (namespace: string) { return function (commitType: string) { @@ -41,10 +43,10 @@ export function createComputedGetterSetter ( {path: string, mutation: string, namespace:string}): GetSetPair { return { get (): any { - return getObjectValueByPath(this.$store.state, path) + return getObjectValueByPath((this as any).$store.state, path) }, set (val: object): void { - this.$store.commit(`${namespace ? namespace + '/' : ''}${mutation}`, val) + (this as any).$store.commit(`${namespace ? namespace + '/' : ''}${mutation}`, val) } } } @@ -80,9 +82,10 @@ export function mapStateToComputedGetterSetter ( // thanks to rsp // https://stackoverflow.com/questions/12303989/cartesian-product-of-multiple-arrays-in-javascript/43053803#43053803 -let cartesianHelper = (a, b) => [].concat(...a.map(a => b.map(b => [].concat(a, b)))) -export function cartesian (a, b, ...c) { - // @ts-ignore can be ignored since cartesian is only recursively called if b si truthy +function cartesianHelper (a: Array<any>, b: Array<any>): Array<Array<any>> { + return ([] as Array<any>).concat(...a.map((a: any) => b.map((b: any) => [].concat(a, b)))) +} +export function cartesian (a: Array<any>, b?: Array<any>, ...c: Array<Array<any>>): Array<Array<any>> { return b ? cartesian(cartesianHelper(a, b), ...c) : a } @@ -93,24 +96,33 @@ export function flatten (list: any[]): any[] { ) } -export function objectifyArray<T> (arr: T[], key = 'pk'): {[key: string]: T} { - return arr.reduce((acc, curr) => { +export function objectifyArray<T, P extends keyof T> (arr: T[], key: P): {[index: string]: T} { + return arr.reduce((acc: any, curr) => { acc[curr[key]] = curr return acc }, {}) } -export function once (fn: Function, context?: object) { - let result - return function () { +interface OnceFunc { + (): any + reset: () => void +} + +export function once (fn: Function, context?: object): OnceFunc { + let result: any + let wrapped = <OnceFunc> function (this: any) { if (!result) { result = fn.apply(context || this, arguments) } return result } + wrapped.reset = function () {result = undefined} + return wrapped } -export function handleError (err, dispatch, fallbackMsg) { +export function handleError (err: Error, dispatch: Dispatch, fallbackMsg: string): any +export function handleError (err: AxiosError, dispatch: Dispatch, fallbackMsg: string): any +export function handleError (err: any, dispatch: Dispatch, fallbackMsg: string): any { if (err.response) { if (err.response.status === 401) { dispatch('logout', 'You\'ve been logged out') diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 7959a1cf..264e7940 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -8,32 +8,6 @@ dependencies: "@babel/highlight" "7.0.0-beta.44" -"@babel/code-frame@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.47.tgz#d18c2f4c4ba8d093a2bcfab5616593bfe2441a27" - dependencies: - "@babel/highlight" "7.0.0-beta.47" - -"@babel/core@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.0.0-beta.47.tgz#b9c164fb9a1e1083f067c236a9da1d7a7d759271" - dependencies: - "@babel/code-frame" "7.0.0-beta.47" - "@babel/generator" "7.0.0-beta.47" - "@babel/helpers" "7.0.0-beta.47" - "@babel/template" "7.0.0-beta.47" - "@babel/traverse" "7.0.0-beta.47" - "@babel/types" "7.0.0-beta.47" - babylon "7.0.0-beta.47" - convert-source-map "^1.1.0" - debug "^3.1.0" - json5 "^0.5.0" - lodash "^4.17.5" - micromatch "^2.3.11" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - "@babel/generator@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.44.tgz#c7e67b9b5284afcf69b309b50d7d37f3e5033d42" @@ -44,52 +18,6 @@ source-map "^0.5.0" trim-right "^1.0.1" -"@babel/generator@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.47.tgz#1835709f377cc4d2a4affee6d9258a10bbf3b9d1" - dependencies: - "@babel/types" "7.0.0-beta.47" - jsesc "^2.5.1" - lodash "^4.17.5" - source-map "^0.5.0" - trim-right "^1.0.1" - -"@babel/helper-annotate-as-pure@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0-beta.47.tgz#354fb596055d9db369211bf075f0d5e93904d6f6" - dependencies: - "@babel/types" "7.0.0-beta.47" - -"@babel/helper-builder-binary-assignment-operator-visitor@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.0.0-beta.47.tgz#d5917c29ee3d68abc2c72f604bc043f6e056e907" - dependencies: - "@babel/helper-explode-assignable-expression" "7.0.0-beta.47" - "@babel/types" "7.0.0-beta.47" - -"@babel/helper-call-delegate@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.0.0-beta.47.tgz#96b7804397075f722a4030d3876f51ec19d8829b" - dependencies: - "@babel/helper-hoist-variables" "7.0.0-beta.47" - "@babel/traverse" "7.0.0-beta.47" - "@babel/types" "7.0.0-beta.47" - -"@babel/helper-define-map@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.0.0-beta.47.tgz#43a9def87c5166dc29630d51b3da9cc4320c131c" - dependencies: - "@babel/helper-function-name" "7.0.0-beta.47" - "@babel/types" "7.0.0-beta.47" - lodash "^4.17.5" - -"@babel/helper-explode-assignable-expression@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.0.0-beta.47.tgz#56b688e282a698f4d1cf135453a11ae8af870a19" - dependencies: - "@babel/traverse" "7.0.0-beta.47" - "@babel/types" "7.0.0-beta.47" - "@babel/helper-function-name@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz#e18552aaae2231100a6e485e03854bc3532d44dd" @@ -98,128 +26,18 @@ "@babel/template" "7.0.0-beta.44" "@babel/types" "7.0.0-beta.44" -"@babel/helper-function-name@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.47.tgz#8057d63e951e85c57c02cdfe55ad7608d73ffb7d" - dependencies: - "@babel/helper-get-function-arity" "7.0.0-beta.47" - "@babel/template" "7.0.0-beta.47" - "@babel/types" "7.0.0-beta.47" - "@babel/helper-get-function-arity@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz#d03ca6dd2b9f7b0b1e6b32c56c72836140db3a15" dependencies: "@babel/types" "7.0.0-beta.44" -"@babel/helper-get-function-arity@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.47.tgz#2de04f97c14b094b55899d3fa83144a16d207510" - dependencies: - "@babel/types" "7.0.0-beta.47" - -"@babel/helper-hoist-variables@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.0.0-beta.47.tgz#ce295d1d723fe22b2820eaec748ed701aa5ae3d0" - dependencies: - "@babel/types" "7.0.0-beta.47" - -"@babel/helper-member-expression-to-functions@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0-beta.47.tgz#35bfcf1d16dce481ef3dec66d5a1ae6a7d80bb45" - dependencies: - "@babel/types" "7.0.0-beta.47" - -"@babel/helper-module-imports@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.47.tgz#5af072029ffcfbece6ffbaf5d9984c75580f3f04" - dependencies: - "@babel/types" "7.0.0-beta.47" - lodash "^4.17.5" - -"@babel/helper-module-transforms@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.0.0-beta.47.tgz#7eff91fc96873bd7b8d816698f1a69bbc01f3c38" - dependencies: - "@babel/helper-module-imports" "7.0.0-beta.47" - "@babel/helper-simple-access" "7.0.0-beta.47" - "@babel/helper-split-export-declaration" "7.0.0-beta.47" - "@babel/template" "7.0.0-beta.47" - "@babel/types" "7.0.0-beta.47" - lodash "^4.17.5" - -"@babel/helper-optimise-call-expression@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0-beta.47.tgz#085d864d0613c5813c1b7c71b61bea36f195929e" - dependencies: - "@babel/types" "7.0.0-beta.47" - -"@babel/helper-plugin-utils@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0-beta.47.tgz#4f564117ec39f96cf60fafcde35c9ddce0e008fd" - -"@babel/helper-regex@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.0.0-beta.47.tgz#b8e3b53132c4edbb04804242c02ffe4d60316971" - dependencies: - lodash "^4.17.5" - -"@babel/helper-remap-async-to-generator@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.0.0-beta.47.tgz#444dc362f61470bd61a745ebb364431d9ca186c2" - dependencies: - "@babel/helper-annotate-as-pure" "7.0.0-beta.47" - "@babel/helper-wrap-function" "7.0.0-beta.47" - "@babel/template" "7.0.0-beta.47" - "@babel/traverse" "7.0.0-beta.47" - "@babel/types" "7.0.0-beta.47" - -"@babel/helper-replace-supers@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.0.0-beta.47.tgz#310b206a302868a792b659455ceba27db686cbb7" - dependencies: - "@babel/helper-member-expression-to-functions" "7.0.0-beta.47" - "@babel/helper-optimise-call-expression" "7.0.0-beta.47" - "@babel/traverse" "7.0.0-beta.47" - "@babel/types" "7.0.0-beta.47" - -"@babel/helper-simple-access@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.0.0-beta.47.tgz#234d754acbda9251a10db697ef50181eab125042" - dependencies: - "@babel/template" "7.0.0-beta.47" - "@babel/types" "7.0.0-beta.47" - lodash "^4.17.5" - "@babel/helper-split-export-declaration@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz#c0b351735e0fbcb3822c8ad8db4e583b05ebd9dc" dependencies: "@babel/types" "7.0.0-beta.44" -"@babel/helper-split-export-declaration@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.47.tgz#e11277855472d8d83baf22f2d0186c4a2059b09a" - dependencies: - "@babel/types" "7.0.0-beta.47" - -"@babel/helper-wrap-function@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.0.0-beta.47.tgz#6528b44a3ccb4f3aeeb79add0a88192f7eb81161" - dependencies: - "@babel/helper-function-name" "7.0.0-beta.47" - "@babel/template" "7.0.0-beta.47" - "@babel/traverse" "7.0.0-beta.47" - "@babel/types" "7.0.0-beta.47" - -"@babel/helpers@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.0.0-beta.47.tgz#f9b42ed2e4d5f75ec0fb2e792c173e451e8d40fd" - dependencies: - "@babel/template" "7.0.0-beta.47" - "@babel/traverse" "7.0.0-beta.47" - "@babel/types" "7.0.0-beta.47" - "@babel/highlight@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.44.tgz#18c94ce543916a80553edcdcf681890b200747d5" @@ -228,348 +46,6 @@ esutils "^2.0.2" js-tokens "^3.0.0" -"@babel/highlight@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.47.tgz#8fbc83fb2a21f0bd2b95cdbeb238cf9689cad494" - dependencies: - chalk "^2.0.0" - esutils "^2.0.2" - js-tokens "^3.0.0" - -"@babel/plugin-proposal-async-generator-functions@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.0.0-beta.47.tgz#571142284708c5ad4ec904d9aa705461a010be53" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - "@babel/helper-remap-async-to-generator" "7.0.0-beta.47" - "@babel/plugin-syntax-async-generators" "7.0.0-beta.47" - -"@babel/plugin-proposal-class-properties@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.0.0-beta.47.tgz#08c1a1dfc92d0f5c37b39096c6fb883e1ca4b0f5" - dependencies: - "@babel/helper-function-name" "7.0.0-beta.47" - "@babel/helper-plugin-utils" "7.0.0-beta.47" - "@babel/helper-replace-supers" "7.0.0-beta.47" - "@babel/plugin-syntax-class-properties" "7.0.0-beta.47" - -"@babel/plugin-proposal-decorators@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.0.0-beta.47.tgz#5e8943c8f8eb3301f911ef0dcd3ed64cf28c723e" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - "@babel/plugin-syntax-decorators" "7.0.0-beta.47" - -"@babel/plugin-proposal-object-rest-spread@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.0.0-beta.47.tgz#e1529fddc88e948868ee1d0edaa27ebd9502322d" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - "@babel/plugin-syntax-object-rest-spread" "7.0.0-beta.47" - -"@babel/plugin-proposal-optional-catch-binding@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.0.0-beta.47.tgz#8c6453919537517ea773bb8f3fceda4250795efa" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - "@babel/plugin-syntax-optional-catch-binding" "7.0.0-beta.47" - -"@babel/plugin-proposal-unicode-property-regex@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.0.0-beta.47.tgz#34d7e4811bdc4f512400bb29d01051842528c8d5" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - "@babel/helper-regex" "7.0.0-beta.47" - regexpu-core "^4.1.4" - -"@babel/plugin-syntax-async-generators@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.0.0-beta.47.tgz#8ab94852bf348badc866af85bd852221f0961256" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-syntax-class-properties@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.0.0-beta.47.tgz#de52bed12fd472c848e1562f57dd4a202fe27f11" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-syntax-decorators@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.0.0-beta.47.tgz#a42f10fcd651940bc475d93b3ac23432b4a8a293" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-syntax-dynamic-import@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.0.0-beta.47.tgz#ee964915014a687701ee8e15c289e31a7c899e60" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-syntax-jsx@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.0.0-beta.47.tgz#f3849d94288695d724bd205b4f6c3c99e4ec24a4" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-syntax-object-rest-spread@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.0.0-beta.47.tgz#21da514d94c138b2261ca09f0dec9abadce16185" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-syntax-optional-catch-binding@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.0.0-beta.47.tgz#0b1c52b066aa36893c41450773a5adb904cd4024" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-arrow-functions@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.0.0-beta.47.tgz#d6eecda4c652b909e3088f0983ebaf8ec292984b" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-async-to-generator@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.0.0-beta.47.tgz#5723816ea1e91fa313a84e6ee9cc12ff31d46610" - dependencies: - "@babel/helper-module-imports" "7.0.0-beta.47" - "@babel/helper-plugin-utils" "7.0.0-beta.47" - "@babel/helper-remap-async-to-generator" "7.0.0-beta.47" - -"@babel/plugin-transform-block-scoped-functions@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.0.0-beta.47.tgz#e422278e06c797b43c45f459d83c7af9d6237002" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-block-scoping@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.0.0-beta.47.tgz#b737cc58a81bea57efd5bda0baef9a43a25859ad" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - lodash "^4.17.5" - -"@babel/plugin-transform-classes@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.0.0-beta.47.tgz#7aff9cbe7b26fd94d7a9f97fa90135ef20c93fb6" - dependencies: - "@babel/helper-annotate-as-pure" "7.0.0-beta.47" - "@babel/helper-define-map" "7.0.0-beta.47" - "@babel/helper-function-name" "7.0.0-beta.47" - "@babel/helper-optimise-call-expression" "7.0.0-beta.47" - "@babel/helper-plugin-utils" "7.0.0-beta.47" - "@babel/helper-replace-supers" "7.0.0-beta.47" - "@babel/helper-split-export-declaration" "7.0.0-beta.47" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.0.0-beta.47.tgz#56ef2a021769a2b65e90a3e12fd10b791da9f3e0" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-destructuring@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.0.0-beta.47.tgz#452b607775fd1c4d10621997837189efc0a6d428" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-dotall-regex@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.0.0-beta.47.tgz#d8da9b706d4bfc68dec9d565661f83e6e8036636" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - "@babel/helper-regex" "7.0.0-beta.47" - regexpu-core "^4.1.3" - -"@babel/plugin-transform-duplicate-keys@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.0.0-beta.47.tgz#4aabeda051ca3007e33a207db08f1a0cf9bd253b" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-exponentiation-operator@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.0.0-beta.47.tgz#930e1abf5db9f4db5b63dbf97f3581ad0be1e907" - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "7.0.0-beta.47" - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-for-of@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.0.0-beta.47.tgz#527d5dc24e4a4ad0fc1d0a3990d29968cb984e76" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-function-name@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.0.0-beta.47.tgz#fb443c81cc77f3206a863b730b35c8c553ce5041" - dependencies: - "@babel/helper-function-name" "7.0.0-beta.47" - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-literals@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.0.0-beta.47.tgz#448fad196f062163684a38f10f14e83315892e9c" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-modules-amd@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.0.0-beta.47.tgz#84564419b11c1be6b9fcd4c7b3a6737f2335aac4" - dependencies: - "@babel/helper-module-transforms" "7.0.0-beta.47" - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-modules-commonjs@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.0.0-beta.47.tgz#dfe5c6d867aa9614e55f7616736073edb3aab887" - dependencies: - "@babel/helper-module-transforms" "7.0.0-beta.47" - "@babel/helper-plugin-utils" "7.0.0-beta.47" - "@babel/helper-simple-access" "7.0.0-beta.47" - -"@babel/plugin-transform-modules-systemjs@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.0.0-beta.47.tgz#8514dbcdfca3345abd690059e7e8544e16ecbf05" - dependencies: - "@babel/helper-hoist-variables" "7.0.0-beta.47" - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-modules-umd@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.0.0-beta.47.tgz#6dcfb9661fdd131b20b721044746a7a309882918" - dependencies: - "@babel/helper-module-transforms" "7.0.0-beta.47" - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-new-target@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.0.0-beta.47.tgz#4b5cb7ce30d7bffa105a1f43ed07d6ae206a4155" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-object-super@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.0.0-beta.47.tgz#ca8e5f326c5011c879f3a6ed749e58bd10fff05d" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - "@babel/helper-replace-supers" "7.0.0-beta.47" - -"@babel/plugin-transform-parameters@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.0.0-beta.47.tgz#46a4236040a6552a5f165fb3ddd60368954b0ddd" - dependencies: - "@babel/helper-call-delegate" "7.0.0-beta.47" - "@babel/helper-get-function-arity" "7.0.0-beta.47" - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-regenerator@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.0.0-beta.47.tgz#86500e1c404055fb98fc82b73b09bd053cacb516" - dependencies: - regenerator-transform "^0.12.3" - -"@babel/plugin-transform-runtime@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.0.0-beta.47.tgz#1700938fa8710909cbf28f7dd39f9b40688b09fd" - dependencies: - "@babel/helper-module-imports" "7.0.0-beta.47" - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-shorthand-properties@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.0.0-beta.47.tgz#00be44c4fad8fe2c00ed18ea15ea3c88dd519dbb" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-spread@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.0.0-beta.47.tgz#3feadb02292ed1e9b75090d651b9df88a7ab5c50" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-sticky-regex@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.0.0-beta.47.tgz#c0aa347d76b5dc87d3b37ac016ada3f950605131" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - "@babel/helper-regex" "7.0.0-beta.47" - -"@babel/plugin-transform-template-literals@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.0.0-beta.47.tgz#5f7b5badf64c4c5da79026aeab03001e62a6ee5f" - dependencies: - "@babel/helper-annotate-as-pure" "7.0.0-beta.47" - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-typeof-symbol@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.0.0-beta.47.tgz#03c612ec09213eb386a81d5fa67c234ee4b2034c" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - -"@babel/plugin-transform-unicode-regex@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.0.0-beta.47.tgz#efed0b2f1dfbf28283502234a95b4be88f7fdcb6" - dependencies: - "@babel/helper-plugin-utils" "7.0.0-beta.47" - "@babel/helper-regex" "7.0.0-beta.47" - regexpu-core "^4.1.3" - -"@babel/preset-env@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.0.0-beta.47.tgz#a3dab3b5fac4de56e3510bdbcb528f1cbdedbe2d" - dependencies: - "@babel/helper-module-imports" "7.0.0-beta.47" - "@babel/helper-plugin-utils" "7.0.0-beta.47" - "@babel/plugin-proposal-async-generator-functions" "7.0.0-beta.47" - "@babel/plugin-proposal-object-rest-spread" "7.0.0-beta.47" - "@babel/plugin-proposal-optional-catch-binding" "7.0.0-beta.47" - "@babel/plugin-proposal-unicode-property-regex" "7.0.0-beta.47" - "@babel/plugin-syntax-async-generators" "7.0.0-beta.47" - "@babel/plugin-syntax-object-rest-spread" "7.0.0-beta.47" - "@babel/plugin-syntax-optional-catch-binding" "7.0.0-beta.47" - "@babel/plugin-transform-arrow-functions" "7.0.0-beta.47" - "@babel/plugin-transform-async-to-generator" "7.0.0-beta.47" - "@babel/plugin-transform-block-scoped-functions" "7.0.0-beta.47" - "@babel/plugin-transform-block-scoping" "7.0.0-beta.47" - "@babel/plugin-transform-classes" "7.0.0-beta.47" - "@babel/plugin-transform-computed-properties" "7.0.0-beta.47" - "@babel/plugin-transform-destructuring" "7.0.0-beta.47" - "@babel/plugin-transform-dotall-regex" "7.0.0-beta.47" - "@babel/plugin-transform-duplicate-keys" "7.0.0-beta.47" - "@babel/plugin-transform-exponentiation-operator" "7.0.0-beta.47" - "@babel/plugin-transform-for-of" "7.0.0-beta.47" - "@babel/plugin-transform-function-name" "7.0.0-beta.47" - "@babel/plugin-transform-literals" "7.0.0-beta.47" - "@babel/plugin-transform-modules-amd" "7.0.0-beta.47" - "@babel/plugin-transform-modules-commonjs" "7.0.0-beta.47" - "@babel/plugin-transform-modules-systemjs" "7.0.0-beta.47" - "@babel/plugin-transform-modules-umd" "7.0.0-beta.47" - "@babel/plugin-transform-new-target" "7.0.0-beta.47" - "@babel/plugin-transform-object-super" "7.0.0-beta.47" - "@babel/plugin-transform-parameters" "7.0.0-beta.47" - "@babel/plugin-transform-regenerator" "7.0.0-beta.47" - "@babel/plugin-transform-shorthand-properties" "7.0.0-beta.47" - "@babel/plugin-transform-spread" "7.0.0-beta.47" - "@babel/plugin-transform-sticky-regex" "7.0.0-beta.47" - "@babel/plugin-transform-template-literals" "7.0.0-beta.47" - "@babel/plugin-transform-typeof-symbol" "7.0.0-beta.47" - "@babel/plugin-transform-unicode-regex" "7.0.0-beta.47" - browserslist "^3.0.0" - invariant "^2.2.2" - semver "^5.3.0" - -"@babel/runtime@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.47.tgz#273f5e71629e80f6cbcd7507503848615e59f7e0" - dependencies: - core-js "^2.5.3" - regenerator-runtime "^0.11.1" - "@babel/template@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f" @@ -579,15 +55,6 @@ babylon "7.0.0-beta.44" lodash "^4.2.0" -"@babel/template@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.47.tgz#0473970a7c0bee7a1a18c1ca999d3ba5e5bad83d" - dependencies: - "@babel/code-frame" "7.0.0-beta.47" - "@babel/types" "7.0.0-beta.47" - babylon "7.0.0-beta.47" - lodash "^4.17.5" - "@babel/traverse@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.44.tgz#a970a2c45477ad18017e2e465a0606feee0d2966" @@ -603,21 +70,6 @@ invariant "^2.2.0" lodash "^4.2.0" -"@babel/traverse@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.47.tgz#0e57fdbb9ff3a909188b6ebf1e529c641e6c82a4" - dependencies: - "@babel/code-frame" "7.0.0-beta.47" - "@babel/generator" "7.0.0-beta.47" - "@babel/helper-function-name" "7.0.0-beta.47" - "@babel/helper-split-export-declaration" "7.0.0-beta.47" - "@babel/types" "7.0.0-beta.47" - babylon "7.0.0-beta.47" - debug "^3.1.0" - globals "^11.1.0" - invariant "^2.2.0" - lodash "^4.17.5" - "@babel/types@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.44.tgz#6b1b164591f77dec0a0342aca995f2d046b3a757" @@ -626,14 +78,6 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" -"@babel/types@7.0.0-beta.47": - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.47.tgz#e6fcc1a691459002c2671d558a586706dddaeef8" - dependencies: - esutils "^2.0.2" - lodash "^4.17.5" - to-fast-properties "^2.0.0" - "@intervolga/optimize-cssnano-plugin@^1.0.5": version "1.0.6" resolved "https://registry.yarnpkg.com/@intervolga/optimize-cssnano-plugin/-/optimize-cssnano-plugin-1.0.6.tgz#be7c7846128b88f6a9b1d1261a0ad06eb5c0fdf8" @@ -657,6 +101,10 @@ version "4.1.4" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.4.tgz#5ca073b330d90b4066d6ce18f60d57f2084ce8ca" +"@types/highlight.js@^9.12.3": + version "9.12.3" + resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.3.tgz#b672cfaac25cbbc634a0fd92c515f66faa18dbca" + "@types/mocha@^5.2.4": version "5.2.5" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.5.tgz#8a4accfc403c124a0bafe8a9fc61a05ec1032073" @@ -665,33 +113,10 @@ version "10.5.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.5.tgz#8e84d24e896cd77b0d4f73df274027e3149ec2ba" -"@vue/babel-preset-app@^3.0.0-rc.10": - version "3.0.0-rc.10" - resolved "https://registry.yarnpkg.com/@vue/babel-preset-app/-/babel-preset-app-3.0.0-rc.10.tgz#da46d88860011dca60954c0c8692bdc68be552a6" - dependencies: - "@babel/plugin-proposal-class-properties" "7.0.0-beta.47" - "@babel/plugin-proposal-decorators" "7.0.0-beta.47" - "@babel/plugin-syntax-dynamic-import" "7.0.0-beta.47" - "@babel/plugin-syntax-jsx" "7.0.0-beta.47" - "@babel/plugin-transform-runtime" "7.0.0-beta.47" - "@babel/preset-env" "7.0.0-beta.47" - "@babel/runtime" "7.0.0-beta.47" - babel-helper-vue-jsx-merge-props "^2.0.3" - babel-plugin-dynamic-import-node "^2.0.0" - babel-plugin-transform-vue-jsx "^4.0.1" - "@vue/cli-overlay@^3.0.0-rc.10": version "3.0.0-rc.10" resolved "https://registry.yarnpkg.com/@vue/cli-overlay/-/cli-overlay-3.0.0-rc.10.tgz#d7fc86e1b1afc1b7ae52875c388a5532c0c10505" -"@vue/cli-plugin-babel@^3.0.0-rc.10": - version "3.0.0-rc.10" - resolved "https://registry.yarnpkg.com/@vue/cli-plugin-babel/-/cli-plugin-babel-3.0.0-rc.10.tgz#616ac0958927106353d37e61def3cf593c5e4a6f" - dependencies: - "@babel/core" "7.0.0-beta.47" - "@vue/babel-preset-app" "^3.0.0-rc.10" - babel-loader "^8.0.0-0" - "@vue/cli-plugin-e2e-nightwatch@^3.0.0-rc.10": version "3.0.0-rc.10" resolved "https://registry.yarnpkg.com/@vue/cli-plugin-e2e-nightwatch/-/cli-plugin-e2e-nightwatch-3.0.0-rc.10.tgz#40597ba93fff523f142eeb284bc30be108c78dcf" @@ -1328,36 +753,6 @@ babel-eslint@^8.2.5: eslint-scope "3.7.1" eslint-visitor-keys "^1.0.0" -babel-helper-vue-jsx-merge-props@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz#22aebd3b33902328e513293a8e4992b384f9f1b6" - -babel-loader@^8.0.0-0: - version "8.0.0-beta.4" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.0-beta.4.tgz#c3fab00696c385c70c04dbe486391f0eb996f345" - dependencies: - find-cache-dir "^1.0.0" - loader-utils "^1.0.2" - mkdirp "^0.5.1" - util.promisify "^1.0.0" - -babel-plugin-dynamic-import-node@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.0.0.tgz#d6fc3f6c5e3bdc34e49c15faca7ce069755c0a57" - dependencies: - babel-plugin-syntax-dynamic-import "^6.18.0" - object.assign "^4.1.0" - -babel-plugin-syntax-dynamic-import@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da" - -babel-plugin-transform-vue-jsx@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-4.0.1.tgz#2c8bddce87a6ef09eaa59869ff1bfbeeafc5f88d" - dependencies: - esutils "^2.0.2" - babel-runtime@^6.18.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" @@ -1369,10 +764,6 @@ babylon@7.0.0-beta.44: version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.44.tgz#89159e15e6e30c5096e22d738d8c0af8a0e8ca1d" -babylon@7.0.0-beta.47: - version "7.0.0-beta.47" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.47.tgz#6d1fa44f0abec41ab7c780481e62fd9aafbdea80" - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -1556,7 +947,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^3.0.0, browserslist@^3.2.8: +browserslist@^3.2.8: version "3.2.8" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6" dependencies: @@ -2022,10 +1413,6 @@ content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" -convert-source-map@^1.1.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" - cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -2062,7 +1449,7 @@ copy-webpack-plugin@^4.5.2: p-limit "^1.0.0" serialize-javascript "^1.4.0" -core-js@^2.4.0, core-js@^2.5.3: +core-js@^2.4.0: version "2.5.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" @@ -3965,7 +3352,7 @@ interpret@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" -invariant@^2.2.0, invariant@^2.2.2: +invariant@^2.2.0: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" dependencies: @@ -4841,7 +4228,7 @@ methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" -micromatch@^2.1.5, micromatch@^2.3.11: +micromatch@^2.1.5: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" dependencies: @@ -6018,10 +5405,6 @@ pretty-error@^2.0.2: renderkid "^2.0.1" utila "~0.4" -private@^0.1.6: - version "0.1.8" - resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" - process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" @@ -6278,26 +5661,14 @@ reduce-css-calc@^2.0.0: css-unit-converter "^1.1.1" postcss-value-parser "^3.3.0" -regenerate-unicode-properties@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz#107405afcc4a190ec5ed450ecaa00ed0cafa7a4c" - dependencies: - regenerate "^1.4.0" - -regenerate@^1.2.1, regenerate@^1.4.0: +regenerate@^1.2.1: version "1.4.0" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" -regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1: +regenerator-runtime@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" -regenerator-transform@^0.12.3: - version "0.12.4" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.12.4.tgz#aa9b6c59f4b97be080e972506c560b3bccbfcff0" - dependencies: - private "^0.1.6" - regex-cache@^0.4.2: version "0.4.4" resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" @@ -6323,37 +5694,16 @@ regexpu-core@^1.0.0: regjsgen "^0.2.0" regjsparser "^0.1.4" -regexpu-core@^4.1.3, regexpu-core@^4.1.4: - version "4.2.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.2.0.tgz#a3744fa03806cffe146dea4421a3e73bdcc47b1d" - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^7.0.0" - regjsgen "^0.4.0" - regjsparser "^0.3.0" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.0.2" - regjsgen@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" -regjsgen@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.4.0.tgz#c1eb4c89a209263f8717c782591523913ede2561" - regjsparser@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" dependencies: jsesc "~0.5.0" -regjsparser@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.3.0.tgz#3c326da7fcfd69fa0d332575a41c8c0cdf588c96" - dependencies: - jsesc "~0.5.0" - relateurl@0.2.x: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" @@ -7331,25 +6681,6 @@ uglifyjs-webpack-plugin@^1.2.4, uglifyjs-webpack-plugin@^1.2.7: webpack-sources "^1.1.0" worker-farm "^1.5.2" -unicode-canonical-property-names-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" - -unicode-match-property-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" - dependencies: - unicode-canonical-property-names-ecmascript "^1.0.4" - unicode-property-aliases-ecmascript "^1.0.4" - -unicode-match-property-value-ecmascript@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz#9f1dc76926d6ccf452310564fd834ace059663d4" - -unicode-property-aliases-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz#5a533f31b4317ea76f17d807fa0d116546111dd0" - union-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" @@ -7450,7 +6781,7 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -util.promisify@1.0.0, util.promisify@^1.0.0, util.promisify@~1.0.0: +util.promisify@1.0.0, util.promisify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" dependencies: diff --git a/grady/settings/default.py b/grady/settings/default.py index a9a9fbee..24cbddfa 100644 --- a/grady/settings/default.py +++ b/grady/settings/default.py @@ -147,9 +147,12 @@ REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES': ( 'djangorestframework_camel_case.parser.CamelCaseJSONParser', ), - # 'JSON_UNDERSCOREIZE': { - # 'no_underscore_before_number': True, - # }, +} + +JSON_CAMEL_CASE = { + 'JSON_UNDERSCOREIZE': { + 'no_underscore_before_number': True, + }, } JWT_AUTH = { diff --git a/requirements.txt b/requirements.txt index 5389e454..3a910671 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,8 +3,10 @@ django-extensions~=2.1 djangorestframework-csv~=2.0.0 djangorestframework-jwt~=1.11.0 djangorestframework~=3.8 +git+https://github.com/robinhundt/djangorestframework-camel-case Django~=2.1 drf-dynamic-fields~=0.3.0 +drf-yasg gevent~=1.3.2 gunicorn~=19.7.0 psycopg2-binary~=2.7.4 -- GitLab From 0128df94a2f12e07f60e15b166dd9a71614b4fb9 Mon Sep 17 00:00:00 2001 From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de> Date: Tue, 14 Aug 2018 10:18:41 +0200 Subject: [PATCH 5/9] Added mips and haskell options in SubmissionType Prog. lang --- core/models.py | 4 ++++ util/importer.py | 28 ++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/core/models.py b/core/models.py index f5e6e2bf..bc33deec 100644 --- a/core/models.py +++ b/core/models.py @@ -109,10 +109,14 @@ class SubmissionType(models.Model): C = 'c' JAVA = 'java' + MIPS = 'mipsasm' + HASKELL = 'haskell' LANGUAGE_CHOICES = ( (C, 'C syntax highlighting'), (JAVA, 'Java syntax highlighting'), + (MIPS, 'Mips syntax highlighting'), + (HASKELL, 'Haskell syntax highlighting'), ) submission_type_id = models.UUIDField(primary_key=True, diff --git a/util/importer.py b/util/importer.py index 736591c6..bbd4c706 100644 --- a/util/importer.py +++ b/util/importer.py @@ -163,13 +163,24 @@ def call_loader(func: Callable) -> None: info(f'{func.__name__} is done.') +def file_suffix_to_lang_name(suffix: str) -> str: + suffix2name = { + 'hs': 'haskell', + 's': 'mipsasm' + } + if suffix not in suffix2name: + return suffix + return suffix2name[suffix] + + def do_load_submission_types(): print( '''For the following import you need three files: - 1) A .csv file where the columns are: id, name, score, (suffix). No + 1) A .csv file where the columns are: id, name, score, (file suffix). No suffix defaults to .c + Supported suffixes: .c , .java , .hs , .s (for mips) 2) A path to a directory where I can find sample solutions named <id>-lsg.c 3) A path to a directory where I can find HTML files with an accurate @@ -204,17 +215,18 @@ def do_load_submission_types(): csv_rows = [row for row in csv.reader(tfile)] for row in csv_rows: - tid, name, score, *lang = (col.strip() for col in row) + tid, name, score, *suffix = (col.strip() for col in row) - if not lang: - lang = '.c' + if not suffix: + suffix = '.c' else: - lang = lang[0] + suffix = suffix[0] - lang = lang.lower().strip('.') + suffix = suffix.lower().strip('.') + lang_name = file_suffix_to_lang_name(suffix) with \ - open(os.path.join(lsg_dir, tid + '.' + lang), + open(os.path.join(lsg_dir, tid + '.' + suffix), encoding='utf-8') as lsg, \ open(os.path.join(desc_dir, tid + '.html'), encoding='utf-8') as desc: @@ -223,7 +235,7 @@ def do_load_submission_types(): 'description': desc.read(), 'solution': lsg.read(), 'full_score': int(score), - 'programming_language': lang + 'programming_language': lang_name } _, created = SubmissionType.objects.update_or_create( name=name, -- GitLab From c7822341595a7a147619f07de9d52c2a5c90b5d0 Mon Sep 17 00:00:00 2001 From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de> Date: Wed, 15 Aug 2018 14:29:03 +0200 Subject: [PATCH 6/9] Fixed final typings/removed WelcomeJumbotron --- frontend/src/components/WelcomeJumbotron.vue | 66 ------------------- .../feedback_list/FeedbackSearchOptions.vue | 2 +- .../src/pages/reviewer/ReviewerStartPage.vue | 5 -- frontend/src/pages/tutor/TutorStartPage.vue | 5 -- frontend/src/store/modules/authentication.ts | 4 +- .../modules/feedback_list/feedback-table.ts | 2 +- .../src/store/modules/submission-notes.ts | 4 +- frontend/src/store/modules/subscriptions.ts | 16 +++-- frontend/src/store/mutations.ts | 7 ++ frontend/src/store/store.ts | 4 +- 10 files changed, 25 insertions(+), 90 deletions(-) delete mode 100644 frontend/src/components/WelcomeJumbotron.vue diff --git a/frontend/src/components/WelcomeJumbotron.vue b/frontend/src/components/WelcomeJumbotron.vue deleted file mode 100644 index 687274a6..00000000 --- a/frontend/src/components/WelcomeJumbotron.vue +++ /dev/null @@ -1,66 +0,0 @@ -<template> - <v-jumbotron :gradient="gradient" dark class="elevation-10" v-if="showJumbotron"> - <v-btn @click="hide" icon class="hide-btn" absolute> - <v-icon> - close - </v-icon> - </v-btn> - <v-container fill-height> - <v-layout align-center> - <v-flex> - <h1 class="display-2 mb-2 text-xs-center">Grady</h1> - <span class="subheading"> - The intention of this tool is to simplify the exam correcting process at the - University of Goettingen. It is deployed as a - <a href="http://www.django-rest-framework.org/" target="_blank">Django Rest</a> - backend with a <a href="https://vuejs.org/" target="_blank">Vue.js</a> frontend - using the awesome <a href="https://next.vuetifyjs.com/en/" target="_blank">Vuetify</a> library.<br/><br/> - To get started with correcting just create a subscription by clicking one of the plus signs in the - <i>Your subscriptions</i> card. - Subscriptions are the mechanism by which the student submissions are distributed. - </span> - <v-divider class="my-5"></v-divider> - <span class="title mx-4">Check out our - <a href="https://gitlab.gwdg.de/j.michal/grady" target="_blank">Git!</a></span> - <span class="title mx-4">Why the name - <a href="https://www.youtube.com/watch?v=VjdgXqKjHvY" target="_blank">Grady?</a></span> - </v-flex> - </v-layout> - </v-container> - </v-jumbotron> -</template> - -<script> -import { createComputedGetterSetter } from '@/util/helpers' -import { uiMut } from '@/store/modules/ui' - -export default { - name: 'welcome-jumbotron', - data () { - return { - gradient: 'to bottom, #1A237E, #5753DD' - } - }, - computed: { - showJumbotron: createComputedGetterSetter({ - path: 'ui.showJumbotron', - mutation: uiMut.SET_SHOW_JUMBOTRON - }) - }, - methods: { - hide () { - this.showJumbotron = false - } - } -} -</script> - -<style scoped> - a { - color: lightgrey; - text-decoration: none; - } - .hide-btn { - right: 0; - } -</style> diff --git a/frontend/src/components/feedback_list/FeedbackSearchOptions.vue b/frontend/src/components/feedback_list/FeedbackSearchOptions.vue index c5ec661f..c2db7e9c 100644 --- a/frontend/src/components/feedback_list/FeedbackSearchOptions.vue +++ b/frontend/src/components/feedback_list/FeedbackSearchOptions.vue @@ -116,7 +116,7 @@ export default { }, methods: { loadTutors () { - if (this.tutors.length === 0) { + if (this.tutors.length === 0 && this.isReviewer) { this.$store.dispatch('getTutors') } } diff --git a/frontend/src/pages/reviewer/ReviewerStartPage.vue b/frontend/src/pages/reviewer/ReviewerStartPage.vue index c049278d..ec560458 100644 --- a/frontend/src/pages/reviewer/ReviewerStartPage.vue +++ b/frontend/src/pages/reviewer/ReviewerStartPage.vue @@ -1,8 +1,5 @@ <template> <div> - <v-flex lg6 md10 xs12 mx-auto class="mt-4"> - <welcome-jumbotron></welcome-jumbotron> - </v-flex> <v-layout row wrap> <v-flex lg5 md9 xs12> <correction-statistics class="ma-4"></correction-statistics> @@ -17,7 +14,6 @@ <script> import CorrectionStatistics from '@/components/CorrectionStatistics' -import WelcomeJumbotron from '@/components/WelcomeJumbotron' import SubscriptionList from '@/components/subscriptions/SubscriptionList' import SubmissionTypesOverview from '@/components/SubmissionTypesOverview' @@ -25,7 +21,6 @@ export default { components: { SubmissionTypesOverview, SubscriptionList, - WelcomeJumbotron, CorrectionStatistics}, name: 'reviewer-start-page' } diff --git a/frontend/src/pages/tutor/TutorStartPage.vue b/frontend/src/pages/tutor/TutorStartPage.vue index 3d57117c..b782282b 100644 --- a/frontend/src/pages/tutor/TutorStartPage.vue +++ b/frontend/src/pages/tutor/TutorStartPage.vue @@ -1,8 +1,5 @@ <template> <div> - <v-flex lg6 md10 xs12 mx-auto class="mt-4"> - <welcome-jumbotron></welcome-jumbotron> - </v-flex> <v-layout row wrap> <v-flex lg5 md9 xs12> <correction-statistics class="ma-4"></correction-statistics> @@ -18,13 +15,11 @@ <script> import SubscriptionList from '@/components/subscriptions/SubscriptionList' import CorrectionStatistics from '@/components/CorrectionStatistics' -import WelcomeJumbotron from '@/components/WelcomeJumbotron' import SubmissionTypesOverview from '@/components/SubmissionTypesOverview' export default { components: { SubmissionTypesOverview, - WelcomeJumbotron, CorrectionStatistics, SubscriptionList}, name: 'tutor-start-page' diff --git a/frontend/src/store/modules/authentication.ts b/frontend/src/store/modules/authentication.ts index 1b8381f5..34cb5c88 100644 --- a/frontend/src/store/modules/authentication.ts +++ b/frontend/src/store/modules/authentication.ts @@ -90,7 +90,7 @@ const authentication: Module<AuthState, any> = { async getJWT (context: ActionContext<AuthState, any>, credentials: Credentials) { try { const token = await api.fetchJWT(credentials) - context.commit(authMut.SET_JWT_TOKEN, token) + context.commit(authMut.SET_JWT_TOKEN, token.token) } catch (error) { let errorMsg if (!error.response) { @@ -110,7 +110,7 @@ const authentication: Module<AuthState, any> = { commit(authMut.SET_REFRESHING_TOKEN, true) try { const token = await api.refreshJWT(state.token) - commit(authMut.SET_JWT_TOKEN, token) + commit(authMut.SET_JWT_TOKEN, token.token) } finally { commit(authMut.SET_REFRESHING_TOKEN, false) commit(authMut.SET_LAST_TOKEN_REFRESH_TRY) diff --git a/frontend/src/store/modules/feedback_list/feedback-table.ts b/frontend/src/store/modules/feedback_list/feedback-table.ts index f005582e..02337c19 100644 --- a/frontend/src/store/modules/feedback_list/feedback-table.ts +++ b/frontend/src/store/modules/feedback_list/feedback-table.ts @@ -66,7 +66,7 @@ const feedbackTable: Module<FeedbackTableState, RootState> = { }, async getFeedbackHistory ({getters, commit, dispatch}) { let data: [Promise<Feedback[]>, Promise<Assignment[]> | undefined] = - [fetchAllFeedback(), getters.isReviewer() ? fetchAllAssignments() : undefined] + [fetchAllFeedback(), getters.isReviewer ? fetchAllAssignments() : undefined] Promise.all<Feedback[], Assignment[] | undefined>(data) .then(([feedbacks, assignments]: [Feedback[], Assignment[]?]) => { diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts index 32a74341..9a4fe315 100644 --- a/frontend/src/store/modules/submission-notes.ts +++ b/frontend/src/store/modules/submission-notes.ts @@ -57,7 +57,7 @@ function initialState (): SubmissionNotesState { }, updatedFeedback: { pk: 0, - score: 0, + score: undefined, feedbackLines: {} }, commentsMarkedForDeletion: {} @@ -86,7 +86,7 @@ const submissionNotes: Module<SubmissionNotesState, RootState> = { }, {}) }, score: state => { - return state.updatedFeedback.score !== null ? state.updatedFeedback.score : state.origFeedback.score + return state.updatedFeedback.score !== undefined ? state.updatedFeedback.score : state.origFeedback.score }, workInProgress: state => { const openEditor = Object.values(state.ui.showEditorOnLine).reduce((acc, curr) => acc || curr, false) diff --git a/frontend/src/store/modules/subscriptions.ts b/frontend/src/store/modules/subscriptions.ts index dab0467f..4888efce 100644 --- a/frontend/src/store/modules/subscriptions.ts +++ b/frontend/src/store/modules/subscriptions.ts @@ -35,7 +35,7 @@ function initialState (): SubscriptionsState { const MAX_NUMBER_OF_ASSIGNMENTS = 2 // noinspection JSCommentMatchesSignature -const subscriptions: Module<SubscriptionsState, RootState> = { +const subscriptionsModule: Module<SubscriptionsState, RootState> = { state: initialState(), getters: { availableTypes (state, getters) { @@ -93,7 +93,8 @@ const subscriptions: Module<SubscriptionsState, RootState> = { 'submission_type': [] } } - let subscriptionsByStage = getters.availableStages.reduce((acc, curr) => { + let subscriptionsByStage = getters.availableStages.reduce((acc: {[p: string]: {[k: string]: Subscription[]}}, + curr: string) => { acc[curr] = subscriptionsByType() return acc }, {}) @@ -101,7 +102,7 @@ const subscriptions: Module<SubscriptionsState, RootState> = { subscriptionsByStage[subscription.feedbackStage][subscription.queryType].push(subscription) }) // sort the resulting arrays in subscriptions lexicographically by their query_keys - const sortSubscriptions = subscriptionsByType => Object.values(subscriptionsByType) + const sortSubscriptions = (subscriptionsByType: {[k: string]: Subscription[]}) => Object.values(subscriptionsByType) .forEach((arr: object[]) => { if (arr.length > 1 && arr[0].hasOwnProperty('queryKey')) { arr.sort((subA, subB) => { @@ -117,7 +118,7 @@ const subscriptions: Module<SubscriptionsState, RootState> = { }) } }) - Object.values(subscriptionsByStage).forEach(subscriptionsByType => { + Object.values(subscriptionsByStage).forEach((subscriptionsByType: any) => { sortSubscriptions(subscriptionsByType) }) return subscriptionsByStage @@ -166,7 +167,6 @@ const subscriptions: Module<SubscriptionsState, RootState> = { commit(subscriptionMuts.SET_SUBSCRIPTION, subscription) return subscription } catch (err) { - console.log(err) handleError(err, dispatch, 'Subscribing unsuccessful') } }, @@ -176,7 +176,6 @@ const subscriptions: Module<SubscriptionsState, RootState> = { commit(subscriptionMuts.SET_SUBSCRIPTIONS, subscriptions) return subscriptions } catch (err) { - console.log(err) handleError(err, dispatch, 'Unable to fetch subscriptions') } }, @@ -245,6 +244,7 @@ const subscriptions: Module<SubscriptionsState, RootState> = { commit(subscriptionMuts.SET_ACTIVE_SUBSCRIPTION_PK, subscriptionPk) let assignmentsPromises = await dispatch('getAssignmentsForActiveSubscription', MAX_NUMBER_OF_ASSIGNMENTS) let createdAssignments = [] + // TODO refactor this since it's very bad to await promises in for loops for (let promise of assignmentsPromises) { try { createdAssignments.push(await promise) @@ -269,6 +269,7 @@ const subscriptions: Module<SubscriptionsState, RootState> = { if (getters.isReviewer) { const stageKeyCartesian = cartesian( getters.availableStages, getters.availableExamTypeQueryKeys) + // @ts-ignore return stageKeyCartesian.map(([stage, key]: [string, string]) => { dispatch('subscribeTo', {stage, type, key}) }) @@ -277,6 +278,7 @@ const subscriptions: Module<SubscriptionsState, RootState> = { case Subscription.QueryTypeEnum.SubmissionType: const stageKeyCartesian = cartesian( getters.availableStages, getters.availableSubmissionTypeQueryKeys) + // @ts-ignore return stageKeyCartesian.map(([stage, key]: [string, string]) => { dispatch('subscribeTo', {stage, type, key}) }) @@ -291,4 +293,4 @@ const subscriptions: Module<SubscriptionsState, RootState> = { } } -export default subscriptions +export default subscriptionsModule diff --git a/frontend/src/store/mutations.ts b/frontend/src/store/mutations.ts index 8c6f165e..252ead24 100644 --- a/frontend/src/store/mutations.ts +++ b/frontend/src/store/mutations.ts @@ -55,6 +55,13 @@ const mutations: MutationTree<RootState> = { ...statistics } }, + [mut.SET_FEEDBACK] (state, feedback) { + Vue.set(state.feedback, feedback.pk, { + ...state.feedback[feedback.pk], + ...feedback, + ofSubmissionType: state.submissionTypes[feedback['ofSubmissionType']] + }) + }, [mut.UPDATE_SUBMISSION_TYPE] (state, submissionType: SubmissionType) { const updatedSubmissionType = { ...state.submissionTypes[submissionType.pk], diff --git a/frontend/src/store/store.ts b/frontend/src/store/store.ts index 6ab9578e..849519fc 100644 --- a/frontend/src/store/store.ts +++ b/frontend/src/store/store.ts @@ -15,7 +15,7 @@ import getters from './getters' import mutations from '@/store/mutations' import {lastInteraction} from '@/store/plugins/lastInteractionPlugin' import { - Exam, + Exam, Feedback, Statistics, StudentInfoForListView, SubmissionNoType, @@ -27,6 +27,7 @@ Vue.use(Vuex) export interface RootState { lastAppInteraction: number examTypes: {[pk: string]: Exam} + feedback: {[pk: string]: Feedback} submissionTypes: {[pk: string]: SubmissionType} submissions: {[pk: string]: SubmissionNoType} students: {[pk: string]: StudentInfoForListView} @@ -39,6 +40,7 @@ export function initialState (): RootState { return { lastAppInteraction: Date.now(), examTypes: {}, + feedback: {}, submissionTypes: {}, submissions: {}, students: {}, -- GitLab From e169cd0ca9f6563193eb933b744aa8c2e3acd76f Mon Sep 17 00:00:00 2001 From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de> Date: Wed, 15 Aug 2018 15:02:28 +0200 Subject: [PATCH 7/9] Added git install to Dockerfile This is needed in order to install the forked repo `djangorestframework-camelizer` from github --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index d9947f19..98f53921 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,8 @@ RUN apk update \ && apk add --virtual build-deps gcc python3-dev musl-dev curl \ && apk add --no-cache postgresql-dev +RUN apk add --no-cache git + COPY requirements.txt . RUN pip install -r requirements.txt && rm -rf /root/.cache -- GitLab From 47d6c72d689618edd6d30d56b2ccc8f17deea4f6 Mon Sep 17 00:00:00 2001 From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de> Date: Sat, 18 Aug 2018 21:00:18 +0200 Subject: [PATCH 8/9] e2e test run in headless mode now in preperation for CI --- frontend/nightwatch.json | 17 +++++++++++++++++ frontend/nightwatch_globals.js | 3 +++ frontend/package.json | 1 + frontend/tests/e2e/specs/test.js | 2 +- frontend/yarn.lock | 4 ++++ 5 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 frontend/nightwatch.json create mode 100644 frontend/nightwatch_globals.js diff --git a/frontend/nightwatch.json b/frontend/nightwatch.json new file mode 100644 index 00000000..261fbe74 --- /dev/null +++ b/frontend/nightwatch.json @@ -0,0 +1,17 @@ +{ + "globals_path": "nightwatch_globals", + "selenium": { + "cli_args": { + "webdriver.gecko.driver": "/usr/bin/geckodriver" + } + }, + "test_settings": { + "chrome": { + "desiredCapabilities": { + "chromeOptions": { + "args": ["headless"] + } + } + } + } +} diff --git a/frontend/nightwatch_globals.js b/frontend/nightwatch_globals.js new file mode 100644 index 00000000..ce0207f9 --- /dev/null +++ b/frontend/nightwatch_globals.js @@ -0,0 +1,3 @@ +module.exports = { + waitForConditionTimeout: 10000 +} diff --git a/frontend/package.json b/frontend/package.json index 3eca1380..dab5a75e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,6 +28,7 @@ "@types/chai": "^4.1.0", "@types/highlight.js": "^9.12.3", "@types/mocha": "^5.2.4", + "@types/nightwatch": "^0.9.8", "@vue/cli-plugin-e2e-nightwatch": "^3.0.0-rc.10", "@vue/cli-plugin-eslint": "^3.0.0-rc.10", "@vue/cli-plugin-typescript": "^3.0.0-rc.10", diff --git a/frontend/tests/e2e/specs/test.js b/frontend/tests/e2e/specs/test.js index 6cfaba24..5720c20e 100644 --- a/frontend/tests/e2e/specs/test.js +++ b/frontend/tests/e2e/specs/test.js @@ -6,7 +6,7 @@ module.exports = { console.log(`URL: ${process.env.VUE_DEV_SERVER_URL}`) browser .url(process.env.VUE_DEV_SERVER_URL) - .waitForElementVisible('#app', 5000) + .waitForElementVisible('#app') .assert.elementCount('img', 1) .end() } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 264e7940..6d751e73 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -109,6 +109,10 @@ version "5.2.5" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.5.tgz#8a4accfc403c124a0bafe8a9fc61a05ec1032073" +"@types/nightwatch@^0.9.8": + version "0.9.8" + resolved "https://registry.yarnpkg.com/@types/nightwatch/-/nightwatch-0.9.8.tgz#ae38fe9922e1541aabb40730771aec5944961786" + "@types/node@^10.5.2": version "10.5.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.5.tgz#8e84d24e896cd77b0d4f73df274027e3149ec2ba" -- GitLab From 3aeeda83bc01a0b62e54d46337fa9211945f04ae Mon Sep 17 00:00:00 2001 From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de> Date: Sat, 18 Aug 2018 22:34:44 +0200 Subject: [PATCH 9/9] added .asm file ending in import / small bug fix in sub-notes store --- frontend/src/store/modules/submission-notes.ts | 6 +++--- util/importer.py | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts index 9a4fe315..ea23fe7b 100644 --- a/frontend/src/store/modules/submission-notes.ts +++ b/frontend/src/store/modules/submission-notes.ts @@ -51,7 +51,7 @@ function initialState (): SubmissionNotesState { hasOrigFeedback: false, origFeedback: { pk: 0, - score: 0, + score: undefined, isFinal: false, feedbackLines: {} }, @@ -158,9 +158,9 @@ const submissionNotes: Module<SubmissionNotesState, RootState> = { if (Object.keys(state.updatedFeedback.feedbackLines || {}).length > 0) { feedback.feedbackLines = state.updatedFeedback.feedbackLines } - if (state.origFeedback.score === null && state.updatedFeedback.score === null) { + if (state.origFeedback.score === undefined && state.updatedFeedback.score === undefined) { throw new Error('You need to give a score.') - } else if (state.updatedFeedback.score !== null) { + } else if (state.updatedFeedback.score !== undefined) { feedback.score = state.updatedFeedback.score } await dispatch('deleteComments') diff --git a/util/importer.py b/util/importer.py index bbd4c706..a1ed1e61 100644 --- a/util/importer.py +++ b/util/importer.py @@ -166,7 +166,8 @@ def call_loader(func: Callable) -> None: def file_suffix_to_lang_name(suffix: str) -> str: suffix2name = { 'hs': 'haskell', - 's': 'mipsasm' + 's': 'mipsasm', + 'asm': 'mipsasm' } if suffix not in suffix2name: return suffix -- GitLab