diff --git a/core/serializers/submission.py b/core/serializers/submission.py index 8366d1735d9cb2a705f183273a96c1dc5ca17af2..a879560b2267f5a39eb41e5901b26e6563981e8c 100644 --- a/core/serializers/submission.py +++ b/core/serializers/submission.py @@ -17,8 +17,14 @@ class SubmissionNoTextFieldsSerializer(DynamicFieldsModelSerializer): fields = ('pk', 'type', 'score', 'final', 'full_score') -class SubmissionSerializer(DynamicFieldsModelSerializer): - type = SubmissionTypeSerializer() +class StudentSubmissionSerializer(DynamicFieldsModelSerializer): + type = SubmissionTypeSerializer( + # exclude solution from Type information + fields=(('pk', + 'name', + 'full_score', + 'description', + 'programming_language'))) feedback = VisibleCommentFeedbackSerializer() tests = TestSerializer(many=True) diff --git a/core/tests/test_student_page.py b/core/tests/test_student_page.py index fa57aea9f949c6a73bb2f06fec1f3faec48e1773..41800def5a6680ab1e317a92f0351d433eef38b9 100644 --- a/core/tests/test_student_page.py +++ b/core/tests/test_student_page.py @@ -209,10 +209,8 @@ class StudentSelfSubmissionsTests(APITestCase): self.submission_list_first_entry['type']['description'], self.student_info.submissions.first().type.description) - def test_submission_data_contains_solution(self): - self.assertEqual( - self.submission_list_first_entry['type']['solution'], - self.student_info.submissions.first().type.solution) + def test_submission_data_not_contains_solution(self): + self.assertNotIn('solution', self.submission_list_first_entry['type']) def test_submission_data_contains_final_status(self): self.assertEqual( diff --git a/core/views/common_views.py b/core/views/common_views.py index ea2fdccbb19751fe267997ece3670c01257f8fbf..ad08eb76b6b0a37d4c7faa8fe27bab67be25e775 100644 --- a/core/views/common_views.py +++ b/core/views/common_views.py @@ -22,7 +22,7 @@ from core.models import ExamType, StudentInfo, SubmissionType from core.permissions import IsReviewer, IsStudent, IsTutorOrReviewer from core.serializers import (ExamSerializer, StudentInfoSerializer, StudentInfoForListViewSerializer, - SubmissionNoTypeSerializer, SubmissionSerializer, + SubmissionNoTypeSerializer, StudentSubmissionSerializer, SubmissionTypeSerializer, TutorSerializer, UserAccountSerializer) @@ -54,7 +54,7 @@ class StudentSelfApiView(generics.RetrieveAPIView): class StudentSelfSubmissionsApiView(generics.ListAPIView): permission_classes = (IsStudent, ) - serializer_class = SubmissionSerializer + serializer_class = StudentSubmissionSerializer def get_queryset(self): return self.request.user.student.submissions diff --git a/frontend/src/api.ts b/frontend/src/api.ts index fbc5f1d58555daf087f6a9c90a0d5a97a7dfac57..20d6b66eb2d9d6706f2b92839945c4fbb7757205 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -68,7 +68,7 @@ export async function fetchAllStudents (): Promise<Array<StudentInfoForListView> return (await ax.get(url)).data } -export async function fetchStudent ({pk, }: +export async function fetchStudent ({pk}: {pk: string}): Promise<StudentInfoForListView> { const url = `/api/student/${pk}/` return (await ax.get(url)).data diff --git a/frontend/src/components/SubmissionType.vue b/frontend/src/components/SubmissionType.vue index 77baa06c517699677d734ef1c2ae1d2370ee8938..c13641650d764aae37f73ee67b944227cb8d1da4 100644 --- a/frontend/src/components/SubmissionType.vue +++ b/frontend/src/components/SubmissionType.vue @@ -18,12 +18,12 @@ ></span> </v-card-text> </v-card> - <v-flex v-else-if="item.title === 'Solution'"> - <pre - class="elevation-2 solution-code pl-2" - :class="programmingLanguage" - ><span v-html="highlightedSolution"></span></pre> - </v-flex> + <div v-else-if="item.title === 'Solution'"> + <pre + class="elevation-2 solution-code pl-2" + :class="programmingLanguage" + ><span v-html="highlightedSolution"></span></pre> + </div> </v-expansion-panel-content> </v-expansion-panel> </v-card> @@ -47,7 +47,8 @@ export default class SubmissionType extends Vue { }) description!: string @Prop({ type: String, - required: true + required: false, + default: '' }) solution!: string @Prop({ type: Number, @@ -80,12 +81,14 @@ export default class SubmissionType extends Vue { { title: 'Description', text: this.description - }, - { - title: 'Solution', - text: this.solution } ] + if (this.solution) { + items.push({ + title: 'Solution', + text: this.solution + }) + } if (this.reverse) { return items.reverse() } else { diff --git a/frontend/src/components/student/ExamInformation.vue b/frontend/src/components/student/ExamInformation.vue index ac0e01485d6c27a006ba323a9a8b0ab2530a2d3b..0c223f43fad10f728b43c30c400dd6e6ae09f195 100644 --- a/frontend/src/components/student/ExamInformation.vue +++ b/frontend/src/components/student/ExamInformation.vue @@ -1,6 +1,9 @@ <template> - <table class="table table-info rounded"> - <tbody> + <table class="table table-info rounded exam-table"> + <tbody v-if="!exam"> + No exam information present + </tbody> + <tbody v-else> <tr> <th>Module</th> <td>{{ exam.moduleReference }}</td> @@ -29,3 +32,9 @@ export default class ExamInformation extends Vue { @Prop(Object) exam!: Exam } </script> + +<style> +.exam-table { + width: 100% +} +</style> diff --git a/frontend/src/components/student/SubmissionList.vue b/frontend/src/components/student/SubmissionList.vue index b7dd7052ce8bea50846df16a99d1440438342410..d06b481eff4956de4411cf84659bdd66b84e546c 100644 --- a/frontend/src/components/student/SubmissionList.vue +++ b/frontend/src/components/student/SubmissionList.vue @@ -4,7 +4,7 @@ hide-actions :headers="headers" :items="submissions" - item-key="type" + item-key="type.pk" > <template slot="items" slot-scope="props"> <td>{{ props.item.type.name }}</td> @@ -32,7 +32,7 @@ export default { { text: 'Task', align: 'left', - value: 'type', + value: 'type.name', sortable: false }, { diff --git a/frontend/src/components/student_list/StudentListMenu.vue b/frontend/src/components/student_list/StudentListMenu.vue index 616c7430afa922f6ff65f167e59ff738028c9167..53a5f0c473a0ef6c661f1911c1abf9a993afda40 100644 --- a/frontend/src/components/student_list/StudentListMenu.vue +++ b/frontend/src/components/student_list/StudentListMenu.vue @@ -14,7 +14,7 @@ <script> import {activateAllStudentAccess, deactivateAllStudentAccess} from '@/api' -import { actions } from '@/store/actions'; +import { actions } from '@/store/actions' export default { name: 'student-list-menu', diff --git a/frontend/src/models.ts b/frontend/src/models.ts index ddf2d43228df82de5198c6e93f1345117d01fd33..ce4b7e97db9022b1a1d3a78c275b6b81eb6e7c11 100644 --- a/frontend/src/models.ts +++ b/frontend/src/models.ts @@ -503,7 +503,7 @@ export interface SubmissionType { * @type {string} * @memberof SubmissionType */ - solution: string + solution?: string /** * * @type {string} diff --git a/frontend/src/pages/base/TutorReviewerBaseLayout.vue b/frontend/src/pages/base/TutorReviewerBaseLayout.vue index e59264d0424b8043ad05ee3b8463c5100ee6e988..898ebe363d448f4162c5f66458d28eca263be57f 100644 --- a/frontend/src/pages/base/TutorReviewerBaseLayout.vue +++ b/frontend/src/pages/base/TutorReviewerBaseLayout.vue @@ -45,7 +45,7 @@ export default { route: '/home' }, { - name: 'Feedback', + name: 'Feedback History', icon: 'feedback', route: '/feedback' } diff --git a/frontend/src/pages/student/StudentLayout.vue b/frontend/src/pages/student/StudentLayout.vue index 6c8ff0d49dc8b4fd2cc62d6328e21d3c20b67b32..079851e5ef67e1396ac1bd585f45567b3fa4db5d 100644 --- a/frontend/src/pages/student/StudentLayout.vue +++ b/frontend/src/pages/student/StudentLayout.vue @@ -20,11 +20,11 @@ <v-divider></v-divider> - <exam-information - :exam="exam" - v-if="!mini" - class="elevation-1 exam-info ma-1" - /> + <exam-information + :exam="exam" + v-if="!mini" + class="elevation-1 exam-info ma-1" + /> <v-list-tile exact v-for="item in submissionNavItems" :key="item.route" :to="item.route"> <v-list-tile-action> <v-icon v-if="!visited[item.id]">assignment</v-icon> @@ -60,10 +60,10 @@ export default { } }, computed: { - moduleReference () { return StudentPage.state.exam.moduleReference }, submissions () { return StudentPage.state.submissionsForList }, exam () { return StudentPage.state.exam }, visited () { return StudentPage.state.visited }, + moduleReference () { return this.exam ? this.exam.moduleReference : 'No exam information' }, mini () { return UI.state.mini }, diff --git a/frontend/src/pages/student/StudentPage.vue b/frontend/src/pages/student/StudentPage.vue index 13729b0a7a62770471369d39d11ccd533f860b2c..d9d2c31faedadbed6939bb4acae98f461e247cdb 100644 --- a/frontend/src/pages/student/StudentPage.vue +++ b/frontend/src/pages/student/StudentPage.vue @@ -12,17 +12,13 @@ </template> <script> -import {mapState} from 'vuex' -import StudentLayout from './StudentLayout.vue' import SubmissionList from '@/components/student/SubmissionList.vue' -import ExamInformation from '@/components/student/ExamInformation.vue' import { StudentPage } from '@/store/modules/student-page' export default { components: { - ExamInformation, - SubmissionList, - StudentLayout}, + SubmissionList + }, name: 'student-page', created: function () { if (!this.loaded) { @@ -33,7 +29,6 @@ export default { }, computed: { studentName () { return StudentPage.state.studentName }, - exam () { return StudentPage.state.exam }, submissions () { return StudentPage.state.submissionsForList }, loaded () { return StudentPage.state.loaded } } diff --git a/frontend/src/pages/student/StudentSubmissionPage.vue b/frontend/src/pages/student/StudentSubmissionPage.vue index a15579dbab33f8e4402724ab13a0675e900cfcb3..d921bb791c8c35d91f9b0941343ca129d44be8ef 100644 --- a/frontend/src/pages/student/StudentSubmissionPage.vue +++ b/frontend/src/pages/student/StudentSubmissionPage.vue @@ -88,8 +88,7 @@ export default { methods: { onRouteMountOrUpdate (routeId) { StudentPage.SET_VISITED({ index: routeId, visited: true }) - // TODO this seems fishy and there probably is the student page bug in here - SubmissionNotes.SET_SUBMISSION(this.$store.state.studentPage.submissionData[this.id]) + SubmissionNotes.SET_SUBMISSION(StudentPage.state.submissionData[routeId]) } }, mounted () { diff --git a/frontend/src/store/actions.ts b/frontend/src/store/actions.ts index f70fa39fe96f3f1541e3e907a2caa0fd9b15eac1..3d5982c34975357d620760a3e7a67afbba5e7658 100644 --- a/frontend/src/store/actions.ts +++ b/frontend/src/store/actions.ts @@ -10,19 +10,19 @@ 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({}) +async function getExamTypes (context: BareActionContext<RootState, RootState>) { + const examTypes = await api.fetchExamTypes() mut.SET_EXAM_TYPES(examTypes) } -async function updateSubmissionTypes( - context: BareActionContext<RootState, RootState>, +async function updateSubmissionTypes ( + context: BareActionContext<RootState, RootState> ) { const submissionTypes = await api.fetchSubmissionTypes() submissionTypes.forEach(type => { mut.UPDATE_SUBMISSION_TYPE(type) }) } -async function getStudents( +async function getStudents ( context: BareActionContext<RootState, RootState>, opt: { studentPks: Array<string>} = { studentPks: [] @@ -42,11 +42,11 @@ async function getStudents( return students } } -async function getTutors() { +async function getTutors () { const tutors = await api.fetchAllTutors() mut.SET_TUTORS(tutors) } -async function getFeedback( +async function getFeedback ( context: BareActionContext<RootState, RootState>, fetchFeedbackArg: { ofSubmission: string } ) { @@ -54,38 +54,34 @@ async function getFeedback( mut.SET_FEEDBACK(feedback) return feedback } -async function getSubmissionFeedbackTest( +async function getSubmissionFeedbackTest ( context: BareActionContext<RootState, RootState>, submissionPkObj: { pk: string } ) { const submission = await api.fetchSubmissionFeedbackTests(submissionPkObj) mut.SET_SUBMISSION(submission) } -async function getStatistics() { +async function getStatistics () { const statistics = await api.fetchStatistics() mut.SET_STATISTICS(statistics) } -function logout( +function logout ( context: BareActionContext<RootState, RootState>, - message = "" + message = '' ) { - // logout actually receives a normal ActionContext, but for some reason vuex-typex mandates a BareActionContext - // 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) } + router.push({ name: 'login' }) + // there should be a better way to solve the issue of resetting the once() function used in subscription module mut.RESET_STATE() FeedbackTable.RESET_STATE() Authentication.RESET_STATE() Subscriptions.RESET_STATE() SubmissionNotes.RESET_STATE() Authentication.SET_MESSAGE(message) - router.push({ name: "login" }) router.go(0) }