Skip to content
Snippets Groups Projects
SubmissionCorrection.vue 7.01 KiB
<template>
  <div>
    <base-annotated-submission>
      <annotated-submission-top-toolbar
        class="mb-1 elevation-1"
        slot="header"
      />
      <template slot="table-content" id='sub-lines'>
        <tr v-for="(code, lineNo) in submission" :key="`${submissionObj.pk}${lineNo}`" :id="`sub-line-${lineNo}`">
          <submission-line
            :code="code"
            :line-no="lineNo"
            @toggleEditor="toggleEditorOnLine(lineNo)"
          >
            <template v-if="showFeedback">
              <div v-if="origFeedback[lineNo]">
                <feedback-comment
                  v-for="(comment, index) in getSortedComments(lineNo)"
                  v-bind="comment"
                  :visibleToStudent="updatedFeedback[lineNo] ? false : comment.visibleToStudent"
                  :line-no="lineNo"
                  :key="index"
                  :deletable="comment.ofTutor === user || isReviewer"
                  @click.native="toggleEditorOnLine(lineNo, comment)"
                />
              </div>
              <feedback-comment
                v-if="updatedFeedback[lineNo]"
                v-bind="updatedFeedback[lineNo]"
                :line-no="lineNo"
                :deletable="true"
                @click.native="toggleEditorOnLine(lineNo, updatedFeedback[lineNo])"
              />
            </template>
            <comment-form
              v-if="showEditorOnLine[lineNo]"
              :feedback="selectedComment[lineNo].text"
              :lineNo="lineNo"
              @collapseFeedbackForm="toggleEditorOnLine(lineNo)"
            >
            </comment-form>
          </submission-line>
        </tr>
      </template>
      <label-selector
        id="feedback-label-selector"
        :assignedToFeedback="true"
        class="mt-1 elevation-1"
        slot="labels"
      />
      <annotated-submission-bottom-toolbar
        class="mt-1 elevation-1"
        slot="footer"
        :loading="loading"
        :fullScore="submissionObj['fullScore']"
        :skippable="assignment !== undefined"
        :feedback="feedbackObj ? feedbackObj : {}"
        @submitFeedback="submitFeedback"
      />
    </base-annotated-submission>
  </div>
</template>

<script>
import { mapState, mapGetters } from 'vuex'
import CommentForm from '@/components/submission_notes/base/CommentForm.vue'
import FeedbackComment from '@/components/submission_notes/base/FeedbackComment.vue'
import AnnotatedSubmissionTopToolbar from '@/components/submission_notes/toolbars/AnnotatedSubmissionTopToolbar'
import AnnotatedSubmissionBottomToolbar from '@/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar'
import BaseAnnotatedSubmission from '@/components/submission_notes/base/BaseAnnotatedSubmission'
import FeedbackLabel from "@/components/feedback_labels/FeedbackLabel.vue"
import { FeedbackLabels as Labels } from '@/store/modules/feedback-labels'
import LabelSelector from '@/components/feedback_labels/LabelSelector.vue'
import SubmissionLine from '@/components/submission_notes/base/SubmissionLine'
import { SubmissionNotes } from '@/store/modules/submission-notes'
import { Authentication } from '@/store/modules/authentication'
import { actions } from '@/store/actions'
import { fetchFeedback } from '@/api'

export default {
  components: {
    SubmissionLine,
    BaseAnnotatedSubmission,
    AnnotatedSubmissionBottomToolbar,
    AnnotatedSubmissionTopToolbar,
    FeedbackComment,
    LabelSelector,
    CommentForm },
  name: 'submission-correction',
  data () {
    return {
      loading: false,
      feedbackShortPollInterval: null
    }
  },
  props: {
    assignment: {
      type: Object
    },
    // either pass in an assignment or a submission and feedback
    submissionWithoutAssignment: {
      type: Object
    },
    feedback: {
      type: Object
    },
    ignoreHiddenState: {
      type: Boolean,
      default: false,
    }
  },
  computed: {
    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 SubmissionNotes.submission
    },
    submissionObj () {
      return this.assignment ? this.assignment.submission : this.submissionWithoutAssignment
    },
    feedbackObj () {
      return this.assignment ? this.assignment.feedback : this.feedback
    }
  },
  methods: {
    toggleEditorOnLine (lineNo, comment = '') {
      SubmissionNotes.TOGGLE_EDITOR_ON_LINE({ lineNo, comment })
    },
    submitFeedback ({ isFinal }) {
      this.loading = true
      SubmissionNotes.submitFeedback({
        isFinal: isFinal
      }).then(feedback => {
        SubmissionNotes.RESET_UPDATED_FEEDBACK()
        SubmissionNotes.SET_ORIG_FEEDBACK(feedback)
        this.$emit('feedbackCreated')
      }).catch(err => {
        // ignore trivial errors as those are handled
        // by an interceptor
        if (err.message.includes("Request failed")) return

        this.$notify({
          title: 'Feedback creation Error!',
          text: err.message,
          type: 'error',
          duration: -1
        })
      }).finally(() => {
        this.$emit('feedbackChanged')
        SubmissionNotes.RESET_MARKED_COMMENTS_FOR_DELETE()
        this.loading = false
      })
    },
    shortPollOrigFeedback () {
      this.feedbackShortPollInterval = setInterval(() => {
        if (this.feedbackObj && this.feedbackObj.ofSubmission) {
          fetchFeedback({ ofSubmission: this.feedbackObj.ofSubmission }).then(feedback => {
            SubmissionNotes.SET_ORIG_FEEDBACK(feedback)
          })
        }
      }, 5e3)
    },
    getSortedComments (lineNo) {
      if (!this.origFeedback || (!this.origFeedback && !this.origFeedback[lineNo])) return new Array()
      let feedback = [...this.origFeedback[lineNo]]

      return feedback.sort((a, b) => {
        const da = new Date(a.modified)
        const db = new Date(b.modified)
        return da.getTime() - db.getTime()
      })
    },
    init () {
      SubmissionNotes.RESET_STATE()
      SubmissionNotes.SET_SUBMISSION(this.submissionObj)
      SubmissionNotes.SET_ORIG_FEEDBACK(this.feedbackObj)
      SubmissionNotes.SET_SHOW_FEEDBACK(this.ignoreHiddenState ? true : !SubmissionNotes.state.hasOrigFeedback)
    }
  },
  watch: {
    assignment: function (newVar, oldVar) {
      this.init()
    },
    submissionWithoutAssignment: function () {
      this.init()
    }
  },
  created () {
    this.init()
    this.shortPollOrigFeedback()
  },
  beforeDestroy () {
    clearInterval(this.feedbackShortPollInterval)
  }
}
</script>

<style scoped>

</style>