Skip to content
Snippets Groups Projects
Commit 10f2a6fc authored by robinwilliam.hundt's avatar robinwilliam.hundt
Browse files

Reviewer can activate/deactivate student access

The reviewer has the option to activate and deactivate all students access via the web interface in the student overview. The corresponding endpoints are additional list routes on the student viewset. Tests are in test_reviewer_viewset.py
parent 5ba3ed77
No related branches found
No related tags found
1 merge request!87Resolve "Student Feedback Serializer/View"
Pipeline #
from .common_serializers import * # noqa
from .feedback import (FeedbackSerializer, FeedbackCommentSerializer,
from .feedback import (FeedbackSerializer, FeedbackCommentSerializer, # noqa
VisibleCommentFeedbackSerializer) # noqa
from .subscription import * # noqa
from .student import * # noqa
......
......@@ -204,4 +204,3 @@ class VisibleCommentFeedbackSerializer(FeedbackSerializer):
model = Feedback
fields = ('pk', 'of_submission', 'is_final', 'score', 'feedback_lines',
'created', 'of_submission_type')
......@@ -22,7 +22,9 @@ class StudentInfoSerializerForListView(DynamicFieldsModelSerializer):
user = serializers.ReadOnlyField(source='user.username')
exam = serializers.ReadOnlyField(source='exam.module_reference')
submissions = SubmissionNoTextFieldsSerializer(many=True)
is_active = serializers.BooleanField(source='user.is_active')
class Meta:
model = StudentInfo
fields = ('pk', 'name', 'user', 'exam', 'submissions', 'matrikel_no')
fields = ('pk', 'name', 'user', 'exam', 'submissions',
'matrikel_no', 'is_active')
......@@ -3,6 +3,7 @@ from rest_framework import status
from rest_framework.test import (APIRequestFactory, APITestCase,
force_authenticate)
from core import models
from core.views import StudentReviewerApiViewSet
from util.factories import make_test_data
......@@ -26,11 +27,18 @@ class StudentPageTests(APITestCase):
'description': 'Very hard',
'solution': 'Impossible!'
}],
'students': [{
'username': 'user01',
'fullname': 'us er01',
'exam': 'TestExam B.Inf.0042'
}],
'students': [
{
'username': 'user01',
'fullname': 'us er01',
'exam': 'TestExam B.Inf.0042'
},
{
'username': 'user02',
'exam': 'TestExam B.Inf.0042'
}
],
'tutors': [{
'username': 'tutor'
}],
......@@ -67,7 +75,7 @@ class StudentPageTests(APITestCase):
self.assertEqual(self.response.status_code, status.HTTP_200_OK)
def test_can_see_all_students(self):
self.assertEqual(1, len(self.response.data))
self.assertEqual(2, len(self.response.data))
def test_submissions_score_is_included(self):
self.assertEqual(self.student.submissions.first().feedback.score,
......@@ -77,3 +85,15 @@ class StudentPageTests(APITestCase):
print(self.response.data[0]['submissions'][0])
self.assertEqual(self.student.submissions.first().type.full_score,
self.response.data[0]['submissions'][0]['full_score'])
def test_can_deactivate_all_students(self):
self.client.force_authenticate(user=self.reviewer)
self.client.post(reverse('student-list') + 'deactivate/')
users = [stud.user for stud in models.StudentInfo.objects.all()]
self.assertTrue(all([not user.is_active for user in users]))
def test_can_activate_all_students(self):
self.client.force_authenticate(user=self.reviewer)
self.client.post(reverse('student-list') + 'activate/')
users = [stud.user for stud in models.StudentInfo.objects.all()]
self.assertTrue(all([user.is_active for user in users]))
......@@ -5,8 +5,8 @@ import logging
from django.conf import settings
from django.db.models import Avg
from rest_framework import generics, mixins, viewsets
from rest_framework.decorators import api_view
from rest_framework import generics, mixins, viewsets, status
from rest_framework.decorators import api_view, list_route
from rest_framework.response import Response
from core import models
......@@ -63,6 +63,22 @@ class StudentReviewerApiViewSet(viewsets.ReadOnlyModelViewSet):
.all()
serializer_class = StudentInfoSerializerForListView
def _set_students_active(self, active):
  • I think StudentInfo.objects.update('user__is_active'=active) would be faster.

  • robinwilliam.hundt @robinwilliam.hundt ·
    Author Maintainer

    That is exactly what i tried at first, unfortunately though:

    The update() method is applied instantly, and the only restriction on the QuerySet that is updated is that it can only update columns in the model’s main table, not on related models. You can’t do this, for example:

    Entry.objects.update(blog__name='foo') # Won't work!

    https://docs.djangoproject.com/en/1.11/ref/models/querysets/#update

  • Ok, didn't know that. But User.objects.filter(role=STUDENT).update(is_active=active) should work then. (I admit this is partly cosmetics).

  • Please register or sign in to reply
for student in self.get_queryset():
user = student.user
user.is_active = active
user.save()
@list_route(methods=['post'])
def deactivate(self, request):
self._set_students_active(False)
return Response(status=status.HTTP_200_OK)
@list_route(methods=['post'])
def activate(self, request):
self._set_students_active(True)
return Response(status=status.HTTP_200_OK)
class ExamApiViewSet(viewsets.ReadOnlyModelViewSet):
""" Gets a list of an individual exam by Id if provided """
......
......@@ -167,4 +167,12 @@ export async function patchComment (comment = {pk: undefined}) {
return (await ax.patch(url, comment)).data
}
export async function activateAllStudentAccess () {
return ax.post('/api/student/activate/')
}
export async function deactivateAllStudentAccess () {
return ax.post('/api/student/deactivate/')
}
export default ax
......@@ -14,6 +14,7 @@
></v-text-field>
<v-card-actions>
<v-btn icon @click="refresh"><v-icon>refresh</v-icon></v-btn>
<student-list-menu/>
</v-card-actions>
</v-card-title>
<v-data-table
......@@ -98,8 +99,10 @@
<script>
import {mapActions, mapState} from 'vuex'
import StudentListMenu from '@/components/student_list/StudentListMenu'
export default {
components: {StudentListMenu},
name: 'student-list',
data () {
return {
......
<template>
<v-menu open-on-hover bottom offset-y>
<v-btn icon slot="activator">
<v-icon>menu</v-icon>
</v-btn>
<v-list>
<v-list-tile v-for="item in items" :key="item.title" @click="item.action">
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</template>
<script>
import {activateAllStudentAccess,
deactivateAllStudentAccess} from '@/api'
export default {
name: 'student-list-menu',
computed: {
studentsActive () {
const firstStudent = Object.values(this.$store.state.students)[0]
return firstStudent ? firstStudent.is_active === true : false
},
items () {
return [
{
title: this.studentsActive
? 'Deactivate student access'
: 'Activate student access',
action: this.changeStudentsAccess
}
]
}
},
methods: {
updateStudentData (fields = []) {
this.$store.dispatch('getStudents', {
studentPks: Object.keys(this.$store.state.students),
fields
}).catch(() => {
this.$notify({
title: 'ERROR',
text: 'Unable to update student data!',
type: 'error'
})
})
},
changeStudentsAccess () {
if (this.studentsActive) {
deactivateAllStudentAccess().then(() => {
this.updateStudentData()
}).catch(() => {
this.$notify({
title: 'ERROR',
text: 'Unable to disable access',
type: 'error'
})
})
} else {
activateAllStudentAccess().then(() => {
this.updateStudentData()
}).catch(() => {
this.$notify({
title: 'ERROR',
text: 'Unable to activate access',
type: 'error'
})
})
}
}
}
}
</script>
<style scoped>
</style>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment