diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3e28a1685c64ba88020c82285948b5c495db2663..d302ff0a128d57a68124d2cb0cf9c90212063e97 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,7 +39,7 @@ test_pytest: <<: *test_definition_virtualenv stage: test services: - - postgres:9.5 + - postgres:9.6 script: - pytest --cov --ds=grady.settings.test core/tests artifacts: diff --git a/frontend/@types/v-clipboard/index.d.ts b/frontend/@types/v-clipboard/index.d.ts index f2dd4459b9daac2e8b5cffd0b18fd0fca3741346..54bf07180256d6c40fc30d44b346cb01ebdf61eb 100644 --- a/frontend/@types/v-clipboard/index.d.ts +++ b/frontend/@types/v-clipboard/index.d.ts @@ -1 +1 @@ -declare module 'v-clipboard'; \ No newline at end of file +declare module 'v-clipboard' diff --git a/frontend/src/components/submission_notes/RouteChangeConfirmation.vue b/frontend/src/components/submission_notes/RouteChangeConfirmation.vue index 362980ee6d7a14ad660374a0f8f25a67d6424e48..9abcee3be3c7f7bb42a39c934596ff7338647448 100644 --- a/frontend/src/components/submission_notes/RouteChangeConfirmation.vue +++ b/frontend/src/components/submission_notes/RouteChangeConfirmation.vue @@ -1,9 +1,9 @@ <template> <v-dialog v-model="dialog" - max-width="fit-content" + max-width="30%" > - <v-card> + <v-card class="text-xs-center"> <v-card-title class="title"> Are you sure? </v-card-title> @@ -19,6 +19,8 @@ </template> <script> +import { SubmissionNotes } from '@/store/modules/submission-notes' + export default { name: 'route-change-confirmation', props: { @@ -40,7 +42,7 @@ export default { }, watch: { nextRoute (newVal, oldVal) { - if (newVal !== oldVal && this.$store.getters['submissionNotes/workInProgress']) { + if (newVal !== oldVal && SubmissionNotes.workInProgress) { this.dialog = true } else { this.nextRoute() diff --git a/frontend/src/components/submission_notes/SubmissionCorrection.vue b/frontend/src/components/submission_notes/SubmissionCorrection.vue index 5339159c8724371c92cb58fbe859c3571c37f766..20378ceed5ac55a06586978e28860d287bd2c863 100644 --- a/frontend/src/components/submission_notes/SubmissionCorrection.vue +++ b/frontend/src/components/submission_notes/SubmissionCorrection.vue @@ -63,9 +63,10 @@ import AnnotatedSubmissionTopToolbar from '@/components/submission_notes/toolbar import AnnotatedSubmissionBottomToolbar from '@/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar' import BaseAnnotatedSubmission from '@/components/submission_notes/base/BaseAnnotatedSubmission' import SubmissionLine from '@/components/submission_notes/base/SubmissionLine' -import {subNotesMut, subNotesNamespace} from '@/store/modules/submission-notes' +import {SubmissionNotes} from '@/store/modules/submission-notes' import RouteChangeConfirmation from '@/components/submission_notes/RouteChangeConfirmation' import {Authentication} from '@/store/modules/authentication' +import { actions } from '@/store/actions' export default { components: { @@ -96,22 +97,20 @@ export default { } }, computed: { - ...mapState({ - showEditorOnLine: state => state.submissionNotes.ui.showEditorOnLine, - selectedComment: state => state.submissionNotes.ui.selectedCommentOnLine, - origFeedback: state => state.submissionNotes.origFeedback.feedbackLines, - updatedFeedback: state => state.submissionNotes.updatedFeedback.feedbackLines, - showFeedback: state => state.submissionNotes.ui.showFeedback - }), - ...mapGetters([ - 'workInProgress' - ]), + showEditorOnLine () { return SubmissionNotes.state.ui.showEditorOnLine }, + selectedComment () { return SubmissionNotes.state.ui.selectedCommentOnLine }, + origFeedback () { return SubmissionNotes.state.origFeedback.feedbackLines }, + updatedFeedback () { return SubmissionNotes.state.updatedFeedback.feedbackLines }, + showFeedback () { return SubmissionNotes.state.ui.showFeedback }, + + workInProgress () { return SubmissionNotes.workInProgress }, + isStudent () { return Authentication.isStudent }, isTutor () { return Authentication.isTutor }, isReviewer () { return Authentication.isReviewer }, user () { return Authentication.state.user.username }, submission () { - return this.$store.getters['submissionNotes/submission'] + return SubmissionNotes.submission }, submissionObj () { return this.assignment ? this.assignment.submission : this.submissionWithoutAssignment @@ -122,15 +121,15 @@ export default { }, methods: { toggleEditorOnLine (lineNo, comment = '') { - this.$store.commit(subNotesNamespace(subNotesMut.TOGGLE_EDITOR_ON_LINE), {lineNo, comment}) + SubmissionNotes.TOGGLE_EDITOR_ON_LINE({lineNo, comment}) }, submitFeedback ({isFinal}) { this.loading = true - this.$store.dispatch(subNotesNamespace('submitFeedback'), { + SubmissionNotes.submitFeedback({ isFinal: isFinal }).then(feedback => { - this.$store.commit(subNotesNamespace(subNotesMut.RESET_UPDATED_FEEDBACK)) - this.$store.commit(subNotesNamespace(subNotesMut.SET_ORIG_FEEDBACK), feedback) + SubmissionNotes.RESET_UPDATED_FEEDBACK() + SubmissionNotes.SET_ORIG_FEEDBACK(feedback) this.$emit('feedbackCreated') }).catch(err => { this.$notify({ @@ -145,16 +144,16 @@ export default { shortPollOrigFeedback () { this.feedbackShortPollInterval = setInterval(() => { 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) + actions.getFeedback({ofSubmission: this.feedbackObj.ofSubmission}).then(feedback => { + SubmissionNotes.SET_ORIG_FEEDBACK(feedback) }) } }, 5e3) }, init () { - this.$store.commit(subNotesNamespace(subNotesMut.RESET_STATE)) - this.$store.commit(subNotesNamespace(subNotesMut.SET_SUBMISSION), this.submissionObj) - this.$store.commit(subNotesNamespace(subNotesMut.SET_ORIG_FEEDBACK), this.feedbackObj) + SubmissionNotes.RESET_STATE() + SubmissionNotes.SET_SUBMISSION(this.submissionObj) + SubmissionNotes.SET_ORIG_FEEDBACK(this.feedbackObj) } }, watch: { diff --git a/frontend/src/components/submission_notes/base/CommentForm.vue b/frontend/src/components/submission_notes/base/CommentForm.vue index 603f4398ff7b973165c873b1812c844dbc50a699..a75e704b2a5e904b20d1e12092b44128a67aabc4 100644 --- a/frontend/src/components/submission_notes/base/CommentForm.vue +++ b/frontend/src/components/submission_notes/base/CommentForm.vue @@ -19,7 +19,7 @@ </template> <script> -import {subNotesMut, subNotesNamespace} from '@/store/modules/submission-notes' +import {SubmissionNotes} from '@/store/modules/submission-notes' export default { name: 'comment-form', @@ -48,7 +48,7 @@ export default { this.$emit('collapseFeedbackForm') }, submitFeedback () { - this.$store.commit(subNotesNamespace(subNotesMut.UPDATE_FEEDBACK_LINE), { + SubmissionNotes.UPDATE_FEEDBACK_LINE({ lineNo: this.lineNo, comment: { text: this.currentFeedback diff --git a/frontend/src/components/submission_notes/base/FeedbackComment.vue b/frontend/src/components/submission_notes/base/FeedbackComment.vue index 29dd835708ecd5e1d01e5bb39b822e4eb327d0e6..a800177667e1c1bb50d1036afdb8af4487118d95 100644 --- a/frontend/src/components/submission_notes/base/FeedbackComment.vue +++ b/frontend/src/components/submission_notes/base/FeedbackComment.vue @@ -38,7 +38,7 @@ <script> import {mapState} from 'vuex' import {UI} from '@/store/modules/ui' -import {subNotesMut, subNotesNamespace} from '@/store/modules/submission-notes' +import {SubmissionNotes} from '@/store/modules/submission-notes' export default { name: 'feedback-comment', @@ -77,9 +77,7 @@ export default { } }, computed: { - ...mapState({ - markedForDeletion: state => state.submissionNotes.commentsMarkedForDeletion - }), + markedForDeletion () { return SubmissionNotes.state.commentsMarkedForDeletion }, parsedCreated () { if (this.created) { return new Date(this.created).toLocaleString() @@ -101,12 +99,12 @@ export default { toggleDeleteComment () { if (this.pk) { if (!this.markedForDeletion.hasOwnProperty(this.pk)) { - this.$store.commit(subNotesNamespace(subNotesMut.MARK_COMMENT_FOR_DELETION), {pk: this.pk}) + SubmissionNotes.MARK_COMMENT_FOR_DELETION({pk: this.pk}) } else { - this.$store.commit(subNotesNamespace(subNotesMut.UN_MARK_COMMENT_FOR_DELETION), {pk: this.pk}) + SubmissionNotes.UN_MARK_COMMENT_FOR_DELETION({pk: this.pk}) } } - this.$store.commit(subNotesNamespace(subNotesMut.DELETE_FEEDBACK_LINE), this.lineNo) + SubmissionNotes.DELETE_FEEDBACK_LINE(this.lineNo) } } } diff --git a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue index f759a584983b86f2a9842d9c2146c77573b08640..10f9b1ace86abad97b240742dd0f4e288600dc11 100644 --- a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue +++ b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue @@ -54,9 +54,9 @@ </template> <script> -import {subNotesMut, subNotesNamespace} from '@/store/modules/submission-notes' +import {SubmissionNotes} from '@/store/modules/submission-notes' import {Authentication} from '@/store/modules/authentication' -import { Subscriptions } from '@/store/modules/subscriptions'; +import { Subscriptions } from '@/store/modules/subscriptions' export default { name: 'annotated-submission-bottom-toolbar', @@ -87,14 +87,14 @@ export default { computed: { score: { get: function () { - return this.$store.getters['submissionNotes/score'] + return SubmissionNotes.score }, set: function (score) { - this.$store.commit(subNotesNamespace(subNotesMut.UPDATE_FEEDBACK_SCORE), Number(score)) + SubmissionNotes.UPDATE_FEEDBACK_SCORE(Number(score)) } }, showFinalCheckbox () { - return !this.$store.getters['submissionNotes/isFeedbackCreation'] || Authentication.isReviewer + return !SubmissionNotes.isFeedbackCreation || Authentication.isReviewer } }, watch: { @@ -108,12 +108,12 @@ export default { methods: { initialFinalStatus () { if (this.$route.name === 'subscription') { - return !this.$store.getters['submissionNotes/isFeedbackCreation'] || Authentication.isReviewer + return !SubmissionNotes.isFeedbackCreation || Authentication.isReviewer } else { if (this.feedback.hasOwnProperty('isFinal')) { return this.feedback.isFinal } else { - return !this.$store.getters['submissionNotes/isFeedbackCreation'] || Authentication.isReviewer + return !SubmissionNotes.isFeedbackCreation || Authentication.isReviewer } } }, diff --git a/frontend/src/components/submission_notes/toolbars/ToggleFeedbackVisibilityButton.vue b/frontend/src/components/submission_notes/toolbars/ToggleFeedbackVisibilityButton.vue index 2c29239e922f02d3eeef0a233c4a3335705e5555..57fef5ddd79cf8c8c343c7c6eaf1353e8a45874b 100644 --- a/frontend/src/components/submission_notes/toolbars/ToggleFeedbackVisibilityButton.vue +++ b/frontend/src/components/submission_notes/toolbars/ToggleFeedbackVisibilityButton.vue @@ -6,16 +6,15 @@ </template> <script> -import {subNotesMut} from '@/store/modules/submission-notes' +import {SubmissionNotes} from '@/store/modules/submission-notes' import {createComputedGetterSetter} from '@/util/helpers' export default { name: 'toggle-feedback-visibility-button', computed: { showFeedback: createComputedGetterSetter({ - path: 'submissionNotes.ui.showFeedback', - mutation: subNotesMut.SET_SHOW_FEEDBACK, - namespace: 'submissionNotes' + path: 'SubmissionNotes.ui.showFeedback', + mutation: SubmissionNotes.SET_SHOW_FEEDBACK }) } } diff --git a/frontend/src/components/subscriptions/SubscriptionCreation.vue b/frontend/src/components/subscriptions/SubscriptionCreation.vue index b7d1561ea0de34be32ac6b89de0cc9ebf1fef1f1..4fd3abea03a6abcc54f1d3af99b25c7fb521b5ad 100644 --- a/frontend/src/components/subscriptions/SubscriptionCreation.vue +++ b/frontend/src/components/subscriptions/SubscriptionCreation.vue @@ -30,7 +30,7 @@ <script> import {Authentication} from '@/store/modules/authentication' -import { Subscriptions } from '@/store/modules/subscriptions'; +import { Subscriptions } from '@/store/modules/subscriptions' const stages = [ { diff --git a/frontend/src/components/subscriptions/SubscriptionForList.vue b/frontend/src/components/subscriptions/SubscriptionForList.vue index 77c4bf121cf0bb7d7786222598979e48ef6bbe93..b85d1bf29be465927b9921079d39167f38635c68 100644 --- a/frontend/src/components/subscriptions/SubscriptionForList.vue +++ b/frontend/src/components/subscriptions/SubscriptionForList.vue @@ -21,7 +21,7 @@ <script lang="ts"> import {Vue, Component, Prop} from 'vue-property-decorator' import {Assignment, Subscription} from '@/models' -import { Subscriptions } from '@/store/modules/subscriptions'; +import { Subscriptions } from '@/store/modules/subscriptions' @Component export default class SubscriptionForList extends Vue { diff --git a/frontend/src/components/subscriptions/SubscriptionsForStage.vue b/frontend/src/components/subscriptions/SubscriptionsForStage.vue index 8c044a5240c4ba5aec11ab2c21c53ec9ac1d9784..1e961cbdf5575776adecd01241263785a1b328d0 100644 --- a/frontend/src/components/subscriptions/SubscriptionsForStage.vue +++ b/frontend/src/components/subscriptions/SubscriptionsForStage.vue @@ -14,7 +14,7 @@ <script> import SubscriptionForList from '@/components/subscriptions/SubscriptionForList' -import { Subscriptions } from '@/store/modules/subscriptions'; +import { Subscriptions } from '@/store/modules/subscriptions' export default { components: { SubscriptionForList diff --git a/frontend/src/pages/student/StudentSubmissionPage.vue b/frontend/src/pages/student/StudentSubmissionPage.vue index e6928c5259bab10ddbf5dcd25af60d66f8750b8d..218a4e7eb6fb0f6356ceb3525f19bfc0a6926259 100644 --- a/frontend/src/pages/student/StudentSubmissionPage.vue +++ b/frontend/src/pages/student/StudentSubmissionPage.vue @@ -60,7 +60,7 @@ import BaseAnnotatedSubmission from '@/components/submission_notes/base/BaseAnno import SubmissionLine from '@/components/submission_notes/base/SubmissionLine' import FeedbackComment from '@/components/submission_notes/base/FeedbackComment' import {studentPageMut} from '@/store/modules/student-page' -import {subNotesMut} from '@/store/modules/submission-notes' +import {SubmissionNotes} from '@/store/modules/submission-notes' import ToggleFeedbackVisibilityButton from '@/components/submission_notes/toolbars/ToggleFeedbackVisibilityButton' import SubmissionTests from '@/components/SubmissionTests' import NonFinalFeedbackAlert from '@/components/student/NonFinalFeedbackAlert' @@ -80,24 +80,22 @@ export default { id: function () { return this.$route.params.id }, - ...mapGetters('submissionNotes', [ - 'submission' - ]), + submission () { return SubmissionNotes.submission }, ...mapState({ submissionType (state) { return state.studentPage.submissionData[this.id].type }, feedback (state) { return state.studentPage.submissionData[this.$route.params.id].feedback }, showFeedback: function (state) { - return state.submissionNotes.ui.showFeedback + return SubmissionNotes.state.ui.showFeedback } }) }, methods: { onRouteMountOrUpdate (routeId) { this.$store.commit(studentPageMut.SET_VISITED, { index: routeId, visited: true }) - this.$store.commit('submissionNotes/' + subNotesMut.SET_SUBMISSION, - this.$store.state.studentPage.submissionData[this.id]) + // TODO this seems fishy and there probably is the student page bug in here + SubmissionNotes.SET_SUBMISSION(this.$store.state.studentPage.submissionData[this.id]) } }, mounted () { diff --git a/frontend/src/store/actions.ts b/frontend/src/store/actions.ts index 31250dade5fa3a4c19a212433bf4feaf5320fa15..95a43c0387e043fa923b385011682f980d5b6678 100644 --- a/frontend/src/store/actions.ts +++ b/frontend/src/store/actions.ts @@ -1,39 +1,39 @@ -import { ActionContext } from "vuex"; -import { BareActionContext, getStoreBuilder } from "vuex-typex"; +import { ActionContext } from 'vuex' +import { BareActionContext, getStoreBuilder } from 'vuex-typex' -import { mutations as mut } from "./mutations"; -import { Authentication } from "@/store/modules/authentication"; -import { subNotesMut } from "@/store/modules/submission-notes"; -import * as api from "@/api"; -import router from "@/router/index"; -import { RootState } from "@/store/store"; -import { FeedbackTable } from '@/store/modules/feedback_list/feedback-table'; -import { Subscriptions } from '@/store/modules/subscriptions'; +import { mutations as mut } from './mutations' +import { Authentication } from '@/store/modules/authentication' +import { SubmissionNotes } from '@/store/modules/submission-notes' +import * as api from '@/api' +import router from '@/router/index' +import { RootState } from '@/store/store' +import { FeedbackTable } from '@/store/modules/feedback_list/feedback-table' +import { Subscriptions } from '@/store/modules/subscriptions' async function getExamTypes(context: BareActionContext<RootState, RootState>) { - const examTypes = await api.fetchExamTypes({}); - mut.SET_EXAM_TYPES(examTypes); + const examTypes = await api.fetchExamTypes({}) + mut.SET_EXAM_TYPES(examTypes) } async function updateSubmissionTypes( context: BareActionContext<RootState, RootState>, fields: Array<string> = [] ) { - const submissionTypes = await api.fetchSubmissionTypes(fields); + const submissionTypes = await api.fetchSubmissionTypes(fields) submissionTypes.forEach(type => { - mut.UPDATE_SUBMISSION_TYPE(type); - }); + mut.UPDATE_SUBMISSION_TYPE(type) + }) } async function getStudents( context: BareActionContext<RootState, RootState>, - opt: { studentPks: Array<string>; fields: Array<string> } = { + opt: { studentPks: Array<string>, fields: Array<string> } = { studentPks: [], fields: [] } ) { if (opt.studentPks.length === 0) { - const students = await api.fetchAllStudents(); - mut.SET_STUDENTS(students); - return students; + const students = await api.fetchAllStudents() + mut.SET_STUDENTS(students) + return students } else { const students = await Promise.all( opt.studentPks.map((pk: string) => @@ -42,33 +42,33 @@ async function getStudents( fields: opt.fields }) ) - ); - students.forEach(student => mut.SET_STUDENT(student)); - return students; + ) + students.forEach(student => mut.SET_STUDENT(student)) + return students } } async function getTutors() { - const tutors = await api.fetchAllTutors(); - mut.SET_TUTORS(tutors); + const tutors = await api.fetchAllTutors() + mut.SET_TUTORS(tutors) } async function getFeedback( context: BareActionContext<RootState, RootState>, fetchFeedbackArg: { ofSubmission: string } ) { - const feedback = await api.fetchFeedback(fetchFeedbackArg); - mut.SET_FEEDBACK(feedback); - return feedback; + const feedback = await api.fetchFeedback(fetchFeedbackArg) + mut.SET_FEEDBACK(feedback) + return feedback } async function getSubmissionFeedbackTest( context: BareActionContext<RootState, RootState>, submissionPkObj: { pk: string } ) { - const submission = await api.fetchSubmissionFeedbackTests(submissionPkObj); - mut.SET_SUBMISSION(submission); + const submission = await api.fetchSubmissionFeedbackTests(submissionPkObj) + mut.SET_SUBMISSION(submission) } async function getStatistics() { - const statistics = await api.fetchStatistics(); - mut.SET_STATISTICS(statistics); + const statistics = await api.fetchStatistics() + mut.SET_STATISTICS(statistics) } function logout( context: BareActionContext<RootState, RootState>, @@ -78,23 +78,23 @@ function logout( // TODO there has to be a better way const { commit, getters, state } = <ActionContext<RootState, RootState>>( context - ); + ) if (Authentication.isStudent) { // change active to false when user logs out // TODO this should belong in auth module - api.changeActiveForUser(Authentication.state.user.pk, false); + api.changeActiveForUser(Authentication.state.user.pk, false) } - mut.RESET_STATE(); + mut.RESET_STATE() FeedbackTable.RESET_STATE() Authentication.RESET_STATE() Subscriptions.RESET_STATE() - commit("submissionNotes/" + subNotesMut.RESET_STATE); - Authentication.SET_MESSAGE(message); - router.push({ name: "login" }); - router.go(0); + SubmissionNotes.RESET_STATE() + Authentication.SET_MESSAGE(message) + router.push({ name: "login" }) + router.go(0) } -const mb = getStoreBuilder<RootState>(); +const mb = getStoreBuilder<RootState>() export const actions = { updateSubmissionTypes: mb.dispatch(updateSubmissionTypes), @@ -105,4 +105,4 @@ export const actions = { getSubmissionFeedbackTest: mb.dispatch(getSubmissionFeedbackTest), getStatistics: mb.dispatch(getStatistics), logout: mb.dispatch(logout) -}; +} diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts index 734acc868778a39bbc3efe80abe967ede7647931..4dfd9d2997e4a7a64475475d257c92a5a9cec4a3 100644 --- a/frontend/src/store/modules/submission-notes.ts +++ b/frontend/src/store/modules/submission-notes.ts @@ -5,24 +5,11 @@ import {nameSpacer} from '@/util/helpers' import {Feedback, FeedbackComment, Submission, SubmissionNoType} from '@/models' import {RootState} from '@/store/store' import {Module} from 'vuex' +import { getStoreBuilder, BareActionContext } from 'vuex-typex' export const subNotesNamespace = nameSpacer('submissionNotes/') -export const subNotesMut = Object.freeze({ - SET_SUBMISSION: 'SET_SUBMISSION', - SET_ORIG_FEEDBACK: 'SET_ORIG_FEEDBACK', - SET_SHOW_FEEDBACK: 'SET_SHOW_FEEDBACK', - UPDATE_FEEDBACK_LINE: 'UPDATE_FEEDBACK_LINE', - UPDATE_FEEDBACK_SCORE: 'UPDATE_FEEDBACK_SCORE', - DELETE_FEEDBACK_LINE: 'DELETE_FEEDBACK_LINE', - TOGGLE_EDITOR_ON_LINE: 'TOGGLE_EDITOR_ON_LINE', - MARK_COMMENT_FOR_DELETION: 'MARK_COMMENT_FOR_DELETION', - UN_MARK_COMMENT_FOR_DELETION: 'UN_MARK_COMMENT_FOR_DELETION', - RESET_UPDATED_FEEDBACK: 'RESET_UPDATED_FEEDBACK', - RESET_STATE: 'RESET_STATE' -}) - -interface SubmissionNotesState { +export interface SubmissionNotesState { submission: SubmissionNoType ui: { showEditorOnLine: {[lineNo: number]: boolean} @@ -64,114 +51,133 @@ function initialState (): SubmissionNotesState { } } -const submissionNotes: Module<SubmissionNotesState, RootState> = { - namespaced: true, - state: initialState(), - getters: { - submissionType: (state, getters, rootState) => { - return rootState.submissionTypes[state.submission.type] - }, - // highlight the submission the reduce the string - // submission.text into an object where the keys are the - // line indexes starting at one and the values the corresponding submission line - // this makes iterating over the submission much more pleasant - submission: (state, getters) => { - const language = getters.submissionType - ? getters.submissionType.programmingLanguage - : 'c' - 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 - }, {}) - }, - score: state => { - 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) - const feedbackWritten = Object.entries(state.updatedFeedback.feedbackLines || {}).length > 0 - return openEditor || feedbackWritten - }, - isFeedbackCreation: state => { - return !state.origFeedback['feedbackStageForUser'] || - state.origFeedback['feedbackStageForUser'] === 'feedback-creation' - } - }, - mutations: { - [subNotesMut.SET_SUBMISSION]: function (state, submission: SubmissionNoType) { - state.submission = submission - }, - [subNotesMut.SET_ORIG_FEEDBACK]: function (state, feedback: Feedback) { - if (feedback) { - state.origFeedback = feedback - state.hasOrigFeedback = true - } - }, - [subNotesMut.SET_SHOW_FEEDBACK]: function (state, val: boolean) { - state.ui.showFeedback = val - }, - [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) { - 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) - Vue.set(state.ui.showEditorOnLine, lineNo, !state.ui.showEditorOnLine[lineNo]) - }, - [subNotesMut.MARK_COMMENT_FOR_DELETION]: function (state, comment) { - Vue.set(state.commentsMarkedForDeletion, comment.pk, comment) - }, - [subNotesMut.UN_MARK_COMMENT_FOR_DELETION]: function (state, comment) { - Vue.delete(state.commentsMarkedForDeletion, comment.pk) - }, - [subNotesMut.RESET_UPDATED_FEEDBACK]: function (state) { - state.updatedFeedback = initialState().updatedFeedback - }, - [subNotesMut.RESET_STATE]: function (state) { - Object.assign(state, initialState()) - } - }, - actions: { - deleteComments: async function ({state}) { - return Promise.all( - Object.values(state.commentsMarkedForDeletion).map(comment => { - return api.deleteComment(comment) - }) - ) - }, - submitFeedback: async function ({state, dispatch, getters}, {isFinal = false}) { - let feedback: Partial<Feedback> = { - isFinal: isFinal, - ofSubmission: state.submission.pk - } - if (Object.keys(state.updatedFeedback.feedbackLines || {}).length > 0) { - feedback.feedbackLines = state.updatedFeedback.feedbackLines - } - if (state.origFeedback.score === undefined && state.updatedFeedback.score === undefined) { - throw new Error('You need to give a score.') - } else if (state.updatedFeedback.score !== undefined) { - 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}> {feedback}) - } - } +const mb = getStoreBuilder<RootState>().module('SubmissionNotes', initialState()) + +const stateGetter = mb.state() + +const submissionTypeGetter = mb.read(function submissionType (state, getters, rootState) { + return rootState.submissionTypes[state.submission.type] +}) +// highlight the submission the reduce the string +// submission.text into an object where the keys are the +// line indexes starting at one and the values the corresponding submission line +// this makes iterating over the submission much more pleasant +const submissionGetter = mb.read(function submission (state, getters) { + const language = getters.submissionType + ? getters.submissionType.programmingLanguage + : 'c' + 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 + }, {}) +}) +const scoreGetter = mb.read(function score (state) { + return state.updatedFeedback.score !== undefined ? state.updatedFeedback.score : state.origFeedback.score +}) +const workInProgressGetter = mb.read(function workInProgress (state) { + const openEditor = Object.values(state.ui.showEditorOnLine).reduce((acc, curr) => acc || curr, false) + const feedbackWritten = Object.entries(state.updatedFeedback.feedbackLines || {}).length > 0 + return openEditor || feedbackWritten +}) +const isFeedbackCreationGetter = mb.read(function isFeedbackCreation (state) { + return !state.origFeedback['feedbackStageForUser'] || + state.origFeedback['feedbackStageForUser'] === 'feedback-creation' +}) + +function SET_SUBMISSION (state: SubmissionNotesState, submission: SubmissionNoType) { + state.submission = submission +} +function SET_ORIG_FEEDBACK (state: SubmissionNotesState, feedback: Feedback) { + if (feedback) { + state.origFeedback = feedback + state.hasOrigFeedback = true + } +} +function SET_SHOW_FEEDBACK (state: SubmissionNotesState, val: boolean) { + state.ui.showFeedback = val +} +function UPDATE_FEEDBACK_LINE (state: SubmissionNotesState, 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) + } +} +function UPDATE_FEEDBACK_SCORE (state: SubmissionNotesState, score: number) { + state.updatedFeedback.score = score +} +function DELETE_FEEDBACK_LINE (state: SubmissionNotesState, lineNo: number) { + if (state.updatedFeedback.feedbackLines) { + // Vue dosn't like objects that are indexed with numbers... + Vue.delete(state.updatedFeedback.feedbackLines, <string><any>lineNo) + } +} +function TOGGLE_EDITOR_ON_LINE (state: SubmissionNotesState, {lineNo, comment}: {lineNo: number, comment: FeedbackComment}) { + Vue.set(state.ui.selectedCommentOnLine, <string><any>lineNo, comment) + Vue.set(state.ui.showEditorOnLine, <string><any> lineNo, !state.ui.showEditorOnLine[lineNo]) +} +function MARK_COMMENT_FOR_DELETION (state: SubmissionNotesState, comment: FeedbackComment) { + Vue.set(state.commentsMarkedForDeletion, comment.pk, comment) +} +function UN_MARK_COMMENT_FOR_DELETION (state: SubmissionNotesState, comment: FeedbackComment) { + Vue.delete(state.commentsMarkedForDeletion, comment.pk) +} +function RESET_UPDATED_FEEDBACK (state: SubmissionNotesState) { + state.updatedFeedback = initialState().updatedFeedback +} +function RESET_STATE (state: SubmissionNotesState) { + Object.assign(state, initialState()) +} + +async function deleteComments ({state}: BareActionContext<SubmissionNotesState, RootState>) { + return Promise.all( + Object.values(state.commentsMarkedForDeletion).map(comment => { + return api.deleteComment(comment) + }) + ) +} +async function submitFeedback ({state}: BareActionContext<SubmissionNotesState, RootState>, {isFinal = false}) { + let feedback: Partial<Feedback> = { + isFinal: isFinal, + ofSubmission: state.submission.pk + } + if (Object.keys(state.updatedFeedback.feedbackLines || {}).length > 0) { + feedback.feedbackLines = state.updatedFeedback.feedbackLines + } + if (state.origFeedback.score === undefined && state.updatedFeedback.score === undefined) { + throw new Error('You need to give a score.') + } else if (state.updatedFeedback.score !== undefined) { + feedback.score = state.updatedFeedback.score + } + await SubmissionNotes.deleteComments() + if (!state.hasOrigFeedback) { + return api.submitFeedbackForAssignment({feedback}) + } else { + feedback.pk = state.origFeedback.pk + return api.submitUpdatedFeedback(<{feedback: Feedback}> {feedback}) } } -export default submissionNotes +export const SubmissionNotes = { + get state () { return stateGetter() }, + get submissionType () { return submissionTypeGetter() }, + get submission () { return submissionGetter() }, + get score () { return scoreGetter() }, + get workInProgress () { return workInProgressGetter() }, + get isFeedbackCreation () { return isFeedbackCreationGetter() }, + + SET_SUBMISSION: mb.commit(SET_SUBMISSION), + SET_ORIG_FEEDBACK: mb.commit(SET_ORIG_FEEDBACK), + SET_SHOW_FEEDBACK: mb.commit(SET_SHOW_FEEDBACK), + UPDATE_FEEDBACK_LINE: mb.commit(UPDATE_FEEDBACK_LINE), + UPDATE_FEEDBACK_SCORE: mb.commit(UPDATE_FEEDBACK_SCORE), + DELETE_FEEDBACK_LINE: mb.commit(DELETE_FEEDBACK_LINE), + TOGGLE_EDITOR_ON_LINE: mb.commit(TOGGLE_EDITOR_ON_LINE), + MARK_COMMENT_FOR_DELETION: mb.commit(MARK_COMMENT_FOR_DELETION), + UN_MARK_COMMENT_FOR_DELETION: mb.commit(UN_MARK_COMMENT_FOR_DELETION), + RESET_UPDATED_FEEDBACK: mb.commit(RESET_UPDATED_FEEDBACK), + RESET_STATE: mb.commit(RESET_STATE), + + deleteComments: mb.dispatch(deleteComments), + submitFeedback: mb.dispatch(submitFeedback) +} diff --git a/frontend/src/store/store.ts b/frontend/src/store/store.ts index c208af27350ae75c80c442856a5a43fb902645bc..f5c32aa8055c0046440a6cc58fcda4e41fc2cc5b 100644 --- a/frontend/src/store/store.ts +++ b/frontend/src/store/store.ts @@ -4,13 +4,13 @@ import createPersistedState from 'vuex-persistedstate' import {getStoreBuilder} from 'vuex-typex' import studentPage from './modules/student-page' -import submissionNotes from './modules/submission-notes' import './modules/ui' import './modules/authentication' import './modules/feedback_list/feedback-search-options' import './modules/feedback_list/feedback-table' import './modules/subscriptions' +import './modules/submission-notes' import './mutations' import './actions' @@ -22,6 +22,7 @@ import {AuthState} from './modules/authentication' import {FeedbackSearchOptionsState, FeedbackSearchOptions} from './modules/feedback_list/feedback-search-options' import {Subscriptions, SubscriptionsState} from './modules/subscriptions' import {FeedbackTableState, FeedbackTable} from './modules/feedback_list/feedback-table' +import { SubmissionNotesState } from './modules/submission-notes' import {lastInteraction} from '@/store/plugins/lastInteractionPlugin' import { @@ -51,7 +52,8 @@ export interface RootState extends RootInitialState{ Authentication: AuthState, FeedbackSearchOptions: FeedbackSearchOptionsState, FeedbackTable: FeedbackTableState, - Subscriptions: SubscriptionsState + Subscriptions: SubscriptionsState, + SubmissionNotes: SubmissionNotesState } export function initialState (): RootInitialState { @@ -78,8 +80,7 @@ export const persistedStateKey = 'grady' const store = getStoreBuilder<RootState>().vuexStore({ strict: process.env.NODE_ENV === 'development', modules: { - studentPage, - submissionNotes + studentPage }, plugins: [ createPersistedState({ diff --git a/frontend/src/util/helpers.ts b/frontend/src/util/helpers.ts index 8d8fe65cd457ba1dfd64a123c41fce51c59d14a8..dae1e7e97a61d633ac55d8b2ab02da7e1feb959b 100644 --- a/frontend/src/util/helpers.ts +++ b/frontend/src/util/helpers.ts @@ -1,6 +1,6 @@ -import {AxiosError} from "axios"; -import {Dispatch} from "vuex"; -import {MutationHandler} from "vuex-typex"; +import {AxiosError} from "axios" +import {Dispatch} from "vuex" +import {MutationHandler} from "vuex-typex" export function nameSpacer (namespace: string) { return function (commitType: string) {