diff --git a/frontend/package.json b/frontend/package.json index 6643f3a1be9581e6ab327c282b6b405da2b4a0e9..b0f89319b0f77f0f8f0493847b140cf39e3f2e83 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "axios": "^0.17.0", - "google-code-prettify": "^1.0.5", + "highlight.js": "^9.12.0", "material-design-icons": "^3.0.1", "v-clipboard": "^1.0.4", "vue": "^2.5.2", diff --git a/frontend/src/components/CorrectionStatistics.vue b/frontend/src/components/CorrectionStatistics.vue index 20f9442319eaca9bbeabe743a138b24b6a60ade8..1b7140c0ecd04cc6425cd68593147e6f982dee7f 100644 --- a/frontend/src/components/CorrectionStatistics.vue +++ b/frontend/src/components/CorrectionStatistics.vue @@ -3,11 +3,12 @@ <v-card-title> <span class="title">Statistics</span> </v-card-title> - <ul class="inline-list mx-3 mb-4"> + <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>Curr. mean score: <span>{{statistics.current_mean_score}}</span></li> </ul> + <v-divider class="mx-2 my-2"></v-divider> <div v-for="(progress, index) in statistics.submission_type_progress" :key="index"> <v-card-title class="py-0"> {{progress.name}} diff --git a/frontend/src/components/SubmissionType.vue b/frontend/src/components/SubmissionType.vue index aa1fbd49679c5945c95cb6b66179d3193ac450bb..6ecb25bc4a33c2f5412bf680bf41340952f19126 100644 --- a/frontend/src/components/SubmissionType.vue +++ b/frontend/src/components/SubmissionType.vue @@ -19,9 +19,9 @@ </v-card> <v-flex v-else-if="item.title === 'Solution'"> <pre - class="prettyprint elevation-2 solution-code pl-2" + class="elevation-2 solution-code pl-2" :class="language" - >{{item.text}}</pre> + ><span v-html="highlightedSolution"></span></pre> </v-flex> </v-expansion-panel-content> </v-expansion-panel> @@ -31,6 +31,7 @@ <script> + import {highlight} from 'highlight.js' export default { name: 'submission-type', props: { @@ -52,7 +53,7 @@ }, language: { type: String, - default: 'lang-c' + default: 'c' }, reverse: { type: Boolean, @@ -85,12 +86,10 @@ } else { return items } + }, + highlightedSolution () { + return highlight(this.language, this.solution, true).value } - }, - mounted () { - this.$nextTick(() => { - window.PR.prettyPrint() - }) } } </script> diff --git a/frontend/src/components/student_list/StudentList.vue b/frontend/src/components/student_list/StudentList.vue index ea229948b0a30ce81234435d7d75ad3ff0ce6b17..cdca5006422f1345fcbb92a6f36260e678ec33f4 100644 --- a/frontend/src/components/student_list/StudentList.vue +++ b/frontend/src/components/student_list/StudentList.vue @@ -56,7 +56,11 @@ <v-btn small round outline class="submission-button" exact - :to="{name: 'submission-side-view', params: {pk: props.item[type.pk].pk}}" + :to="{name: 'submission-side-view', params: { + studentPk: props.item.pk, + submissionPk: props.item[type.pk].pk + }}" + :color="props.item[type.pk].final ? 'green darken-2' : 'grey'" > {{props.item[type.pk].score}} </v-btn> diff --git a/frontend/src/components/submission_notes/SubmissionCorrection.vue b/frontend/src/components/submission_notes/SubmissionCorrection.vue index cffeef17d68a664a29b74012e704bdfb7c240c6f..89a89cf46ddb8ce2312af4fe55ad3d935897fd22 100644 --- a/frontend/src/components/submission_notes/SubmissionCorrection.vue +++ b/frontend/src/components/submission_notes/SubmissionCorrection.vue @@ -127,8 +127,9 @@ this.loading = true this.$store.dispatch(subNotesNamespace('submitFeedback'), { isFinal: isFinal - }).then(() => { - this.$store.commit(subNotesNamespace(subNotesMut.RESET_STATE)) + }).then(feedback => { + this.$store.commit(subNotesNamespace(subNotesMut.RESET_UPDATED_FEEDBACK)) + this.$store.commit(subNotesNamespace(subNotesMut.SET_ORIG_FEEDBACK), feedback) this.$emit('feedbackCreated') }).catch(err => { this.$notify({ @@ -158,15 +159,9 @@ watch: { assignment: function () { this.init() - this.$nextTick(() => { - window.PR.prettyPrint() - }) }, submissionWithoutAssignment: function () { this.init() - this.$nextTick(() => { - window.PR.prettyPrint() - }) } }, created () { @@ -175,11 +170,6 @@ }, beforeDestroy () { clearInterval(this.feedbackShortPollInterval) - }, - mounted () { - this.$nextTick(() => { - window.PR.prettyPrint() - }) } } </script> diff --git a/frontend/src/components/submission_notes/base/SubmissionLine.vue b/frontend/src/components/submission_notes/base/SubmissionLine.vue index 1325cb68310ca0156cef784bcc37fedbfe4a53b6..e3cbfdb2fa635342922304729aa13612dac45253 100644 --- a/frontend/src/components/submission_notes/base/SubmissionLine.vue +++ b/frontend/src/components/submission_notes/base/SubmissionLine.vue @@ -1,19 +1,20 @@ <template> - <div> - <td class="line-number-cell"> - <v-btn - block - class="line-number-btn" - @click="toggleEditor" - > - {{ lineNo }} - </v-btn> - </td> - <td class="code-cell-content pl-2"> - <pre class="prettyprint" :class="codeLanguage">{{ code }}</pre> - <slot/> - </td> - </div> + <div> + <td class="line-number-cell"> + <v-btn + block + class="line-number-btn" + flat + @click="toggleEditor" + > + {{ lineNo }} + </v-btn> + </td> + <td class="code-cell-content pl-2"> + <span v-html="code" class="code-line"></span> + <slot/> + </td> + </div> </template> <script> @@ -46,19 +47,13 @@ vertical-align: top; } - pre.prettyprint { - padding: 0; - border: 0; - white-space: pre-wrap; - } - .code-cell-content { width: 100%; } - code { - width: 100%; - box-shadow: None; + .code-line { + white-space: pre-wrap; + font-family: monospace; } diff --git a/frontend/src/main.js b/frontend/src/main.js index cb7060e5c0c5f3871059e71cd53d780501b930a0..a6045fb87190e12ba63448203bc300e7d6e3b02e 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -10,8 +10,7 @@ import Cliboard from 'v-clipboard' import 'vuetify/dist/vuetify.min.css' import 'material-design-icons/iconfont/material-icons.css' -import 'google-code-prettify/bin/prettify.min' -import 'google-code-prettify/bin/prettify.min.css' +import 'highlight.js/styles/default.css' Vue.use(Vuetify) Vue.use(Cliboard) diff --git a/frontend/src/pages/StudentSubmissionSideView.vue b/frontend/src/pages/StudentSubmissionSideView.vue index a864bd339cb224f2aec8c404697563decf5c7f9f..6085f04dbbf92931c0791591ff77440be9e4c7da 100644 --- a/frontend/src/pages/StudentSubmissionSideView.vue +++ b/frontend/src/pages/StudentSubmissionSideView.vue @@ -3,6 +3,7 @@ <submission-correction :submission-without-assignment="submission" :feedback="submission.feedback" + @feedbackCreated="refresh" ></submission-correction> <submission-tests :tests="submission.tests" @@ -28,9 +29,9 @@ function onRouteEnterOrUpdate (to, from, next) { const toIsSubmissionSideView = to.matched.some(route => route.meta.submissionSideView) if (toIsSubmissionSideView) { - let submission = store.state.submissions[to.params.pk] + let submission = store.state.submissions[to.params.submissionPk] if (!submission) { - store.dispatch('getSubmissionFeedbackTest', {pk: to.params.pk}).then(() => { + store.dispatch('getSubmissionFeedbackTest', {pk: to.params.submissionPk}).then(() => { VueInstance.$nextTick(() => { next() }) @@ -58,7 +59,7 @@ name: 'student-submission-side-view', computed: { submissionPk () { - return this.$route.params['pk'] + return this.$route.params['submissionPk'] }, submission () { return this.$store.state.submissions[this.submissionPk] @@ -67,6 +68,16 @@ return this.$store.state.submissionTypes[this.submission.type] } }, + methods: { + refresh () { + const studentPk = this.$route.params.studentPk + if (studentPk) { + this.$store.dispatch('getStudents', {studentPks: [studentPk]}) + } + const submissionPk = this.$route.params.submissionPk + this.$store.dispatch('getSubmissionFeedbackTest', {pk: submissionPk}) + } + }, beforeRouteEnter (to, from, next) { onRouteEnterOrUpdate(to, from, next) }, diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index cba803b797b1eeab697fd9fd4818ec11bcd9ef87..34ab7d00f0b359a4183cda382635aa9c087e5afc 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -131,7 +131,7 @@ const router = new Router({ component: StudentListHelpCard }, { - path: 'submission/:pk', + path: 'student/:studentPk/submission/:submissionPk', name: 'submission-side-view', component: StudentSubmissionSideView, meta: { diff --git a/frontend/src/store/modules/authentication.js b/frontend/src/store/modules/authentication.js index 43b860652ad37db65db9397817ef50f96ad24ee6..7f5fb0bca89224cef92988e83186a6628200262e 100644 --- a/frontend/src/store/modules/authentication.js +++ b/frontend/src/store/modules/authentication.js @@ -4,7 +4,7 @@ import gradySays from '../grady_speak' function initialState () { return { token: sessionStorage.getItem('token'), - lastTokenRefreshTry: 0, + lastTokenRefreshTry: Date.now(), refreshingToken: false, username: '', jwtTimeDelta: 0, diff --git a/frontend/src/store/modules/submission-notes.js b/frontend/src/store/modules/submission-notes.js index e76845362333840ba82908dbd8d6794256f8bfa9..cfcbee822183979943a6ed1e57555c745cdc59bd 100644 --- a/frontend/src/store/modules/submission-notes.js +++ b/frontend/src/store/modules/submission-notes.js @@ -1,4 +1,5 @@ import Vue from 'vue' +import hljs from 'highlight.js' import * as api from '@/api' import {nameSpacer} from '@/util/helpers' @@ -11,6 +12,7 @@ export const subNotesMut = Object.freeze({ UPDATE_FEEDBACK_SCORE: 'UPDATE_FEEDBACK_SCORE', DELETE_FEEDBACK_LINE: 'DELETE_FEEDBACK_LINE', TOGGLE_EDITOR_ON_LINE: 'TOGGLE_EDITOR_ON_LINE', + RESET_UPDATED_FEEDBACK: 'RESET_UPDATED_FEEDBACK', RESET_STATE: 'RESET_STATE' }) @@ -44,7 +46,8 @@ const submissionNotes = { // line indexes starting at one and the values the corresponding submission line // this makes iterating over the submission much more pleasant submission: state => { - return state.submission.text.split('\n').reduce((acc, cur, index) => { + const highlighted = hljs.highlight('c', state.submission.text, true).value + return highlighted.split('\n').reduce((acc, cur, index) => { acc[index + 1] = cur return acc }, {}) @@ -82,6 +85,9 @@ const submissionNotes = { Vue.set(state.ui.selectedCommentOnLine, lineNo, comment) Vue.set(state.ui.showEditorOnLine, lineNo, !state.ui.showEditorOnLine[lineNo]) }, + [subNotesMut.RESET_UPDATED_FEEDBACK]: function (state) { + state.updatedFeedback = initialState().updatedFeedback + }, [subNotesMut.RESET_STATE]: function (state) { Object.assign(state, initialState()) } diff --git a/frontend/src/store/mutations.js b/frontend/src/store/mutations.js index 0fadd7f7fe75569074ef31899df59c3659724318..0a5c9e721f213f43b510213fcfb1619364a550d3 100644 --- a/frontend/src/store/mutations.js +++ b/frontend/src/store/mutations.js @@ -20,6 +20,7 @@ export const mut = Object.freeze({ SET_FEEDBACK: 'SET_FEEDBACK', MAP_FEEDBACK_OF_SUBMISSION_TYPE: 'MAP_FEEDBACK_OF_SUBMISSION_TYPE', UPDATE_SUBMISSION_TYPE: 'UPDATE_SUBMISSION_TYPE', + UPDATE_SUBMISSION: 'UPDATE_SUBMISSION', RESET_STATE: 'RESET_STATE' }) @@ -90,6 +91,9 @@ const mutations = { } Vue.set(state.submissionTypes, submissionType.pk, updatedSubmissionType) }, + [mut.UPDATE_SUBMISSION] (state, {submissionPk, payload, key}) { + Vue.set(state.submissions[submissionPk], key, payload) + }, [mut.ADD_ASSIGNMENT_TO_SUBSCRIPTION_QUEUE] (state, {assignment}) { let subscription = state.subscriptions[assignment.subscription] subscription['assignments'].push(assignment) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index beee8ae197be293a93f6f5e6da87da2b57843d28..ba5c484cc38d4ae85f467e4e4141a1ff0b34ae9c 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2802,10 +2802,6 @@ globby@^7.1.1: pify "^3.0.0" slash "^1.0.0" -google-code-prettify@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/google-code-prettify/-/google-code-prettify-1.0.5.tgz#9f477f224dbfa62372e5ef803a7e157410400084" - graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -2948,6 +2944,10 @@ he@1.1.1, he@1.1.x, he@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" +highlight.js@^9.12.0: + version "9.12.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" diff --git a/grady/settings/default.py b/grady/settings/default.py index 027d096139a0e2097acccc6617249be42042ea7b..ac672bd7dd49982aa751d57bf60c5052f5c24faf 100644 --- a/grady/settings/default.py +++ b/grady/settings/default.py @@ -156,7 +156,7 @@ REST_FRAMEWORK = { } JWT_AUTH = { - 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=600), + 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=3600), 'JWT_ALLOW_REFRESH': True, }