def destroy(self, request, *args, **kwargs): """Delete an existing assignment from a course. Arguments: request -- request data course_id -- the course ID of course this assignment belongs to pk -- assignment ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when the assignment or course does not exist unauthorized -- when the user is not logged in forbidden -- when the user cannot delete the assignment On success: success -- with a message that the course was deleted """ assignment_id, = utils.required_typed_params(kwargs, (int, 'pk')) course_id, = utils.required_typed_params(request.query_params, (int, 'course_id')) assignment = Assignment.objects.get(pk=assignment_id) course = Course.objects.get(pk=course_id) request.user.check_permission('can_delete_assignment', course) assignment.courses.remove(course) assignment.save() # If the assignment is only connected to one course, delete it completely if assignment.courses.count() == 0: assignment.delete() return response.success(description='Successfully deleted the assignment.') else: return response.success(description='Successfully removed the assignment from {}.'.format(str(course)))
def destroy(self, request, *args, **kwargs): """Delete an existing course group. Arguments: request -- request data group_name -- name of the course pk -- course ID Returns: On failure: not found -- when the course does not exists unauthorized -- when the user is not logged in forbidden -- when the user is not in the course On success: success -- with a message that the course group was deleted """ course_id, = utils.required_typed_params(kwargs, (int, 'pk')) name, = utils.required_typed_params(request.query_params, (str, 'group_name')) course = Course.objects.get(pk=course_id) request.user.check_permission('can_delete_course_user_group', course) group = Group.objects.get(name=name, course=course) group.delete() return response.success(description='Successfully deleted course group.')
def retrieve(self, request, pk=0): """Get the permissions of a user connected to a course / assignment. Arguments: request -- the request that was sent course_id -- course ID assignment_id -- assignment ID pk -- user ID (0 = logged in user) Returns: On failure: unauthorized -- when the user is not logged in not found -- when the course is not found forbidden -- when the user is not in the course On success: success -- with a list of the permissions """ if int(pk) == 0: pk = request.user.id user = User.objects.get(pk=pk) # Return course permissions if course_id is set try: course_id, = utils.required_typed_params(request.query_params, (int, 'course_id')) course = Course.objects.get(pk=course_id) request.user.check_participation(course) if user != request.user: # TODO: P Is this the right permission request.user.check_permission('can_edit_course_roles', course) return response.success({ 'role': permissions.serialize_course_permissions(request.user, course) }) # Return assignment permissions if assignment_id is set except (VLEMissingRequiredKey, VLEParamWrongType): assignment_id, = utils.required_typed_params( request.query_params, (int, 'assignment_id')) assignment = Assignment.objects.get(pk=assignment_id) request.user.check_can_view(assignment) if user != request.user: # TODO: P Add a permission for this request.user.check_permission('can_view_all_journals', course) return response.success({ 'role': permissions.serialize_assignment_permissions( request.user, assignment) })
def destroy(self, request, *args, **kwargs): """Delete an existing assignment from a course. Arguments: request -- request data course_id -- the course ID of course this assignment belongs to pk -- assignment ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when the assignment or course does not exist unauthorized -- when the user is not logged in forbidden -- when the user cannot delete the assignment On success: success -- with a message that the course was deleted """ assignment_id, = utils.required_typed_params(kwargs, (int, 'pk')) course_id, = utils.required_typed_params(request.query_params, (int, 'course_id')) assignment = Assignment.objects.get(pk=assignment_id) course = Course.objects.get(pk=course_id) request.user.check_permission('can_delete_assignment', course) intersecting_assignment_lti_id = assignment.get_course_lti_id(course) if intersecting_assignment_lti_id: if assignment.active_lti_id == intersecting_assignment_lti_id and len( assignment.lti_id_set) > 1: return response.bad_request( 'This assignment cannot be removed from this course, since it is' + ' currently configured for grade passback to the LMS') course.assignment_lti_id_set.remove(intersecting_assignment_lti_id) assignment.lti_id_set.remove(intersecting_assignment_lti_id) course.save() assignment.courses.remove(course) assignment.save() if assignment.active_lti_id is not None and assignment.active_lti_id in course.assignment_lti_id_set: course.assignment_lti_id_set.remove(assignment.active_lti_id) course.save() # If the assignment is only connected to one course, delete it completely if assignment.courses.count() == 0: assignment.delete() return response.success( description='Successfully deleted the assignment.') else: return response.success( description='Successfully removed the assignment from {}.'. format(str(course)))
def list(self, request): """Get the grade history of an entry. Arguments: request -- request data entry_id -- entry ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when the course does not exist forbidden -- when its not their own journal, or the user is not allowed to grade that journal On success: success -- with all historic grades corresponding to the entry """ entry_id, = utils.required_typed_params(request.query_params, (int, "entry_id")) entry = Entry.objects.get(pk=entry_id) assignment = entry.node.journal.assignment request.user.check_permission('can_view_grade_history', assignment) grade_history = Grade.objects.filter(entry=entry) return response.success({ 'grade_history': GradeHistorySerializer(grade_history, many=True).data })
def destroy(self, request, pk): """Remove a user from the course. Deletes a test user if no participations remain. request -- request data user_id -- user ID pk -- course ID """ user_id, = utils.required_typed_params(request.query_params, (int, 'user_id')) course = Course.objects.get(pk=pk) request.user.check_permission('can_delete_course_users', course) user = User.objects.get(pk=user_id) participation = Participation.objects.get(user=user, course=course) participation.delete() if user.is_test_student and not Participation.objects.filter( user=user.pk).exists(): user.delete() return response.success( description='Successfully removed user from course.')
def upcoming(self, request): """Get upcoming deadlines for the requested user. Arguments: request -- request data course_id -- course ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when the course does not exist On success: success -- upcoming assignments """ try: course_id, = utils.required_typed_params(request.query_params, (int, 'course_id')) course = Course.objects.get(pk=course_id) courses = [course] except (VLEMissingRequiredKey, VLEParamWrongType): course = None courses = request.user.participations.all() now = timezone.now() query = Assignment.objects.filter( Q(lock_date__gt=now) | Q(lock_date=None), courses__in=courses ).distinct() viewable = [assignment for assignment in query if request.user.can_view(assignment)] upcoming = AssignmentSerializer(viewable, context={'user': request.user, 'course': course}, many=True).data return response.success({'upcoming': upcoming})
def destroy(self, request, *args, **kwargs): """Delete an existing comment from an entry. Arguments: request -- request data pk -- comment ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when the comment or author does not exist forbidden -- when the user cannot delete the assignment On success: success -- with a message that the comment was deleted """ comment_id, = utils.required_typed_params(kwargs, (int, 'pk')) comment = Comment.objects.get(pk=comment_id) journal = comment.entry.node.journal request.user.check_can_view(journal) if not (request.user.is_superuser or request.user.id == comment.author.id): return response.forbidden(description='You are not allowed to delete this comment.') comment.delete() return response.success(description='Successfully deleted comment.')
def destroy(self, request, pk): """Delete course role. Arguments: request -- request data name -- role name pk -- course ID Returns: On failure: unauthorized -- when the user is not logged in forbidden -- when the user is not in the course forbidden -- when the user is unauthorized to edit its roles On success: success -- newly created course """ name, = utils.required_typed_params(request.query_params, (str, 'name')) course = Course.objects.get(pk=pk) # Users can only delete course roles with can_edit_course_roles request.user.check_permission('can_edit_course_roles', course) if name in ['Student', 'TA', 'Teacher']: return response.bad_request( 'Default roles "Student", "TA" and "Teacher" cannot be deleted.' ) Role.objects.get(name=name, course=pk).delete() return response.success( description='Successfully deleted role from course.')
def list(self, request): """Get the groups from a course for the user. Arguments: request -- request data course_id -- course ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when the course does not exists forbidden -- when the user is not part of the course On success: success -- with the group data """ course_id, = utils.required_typed_params(request.query_params, (int, 'course_id')) course = Course.objects.get(pk=course_id) check_can_view_groups(request.user, course) queryset = Group.objects.filter(course=course) serializer = GroupSerializer(queryset, many=True, context={'user': request.user, 'course': course}) return response.success({'groups': serializer.data})
def partial_update(self, request, *args, **kwargs): """Update the preferences of a user. Arguments: request -- request data pk -- user ID Returns: On failure: unauthorized -- when the user is not logged in forbidden -- when the user is not superuser or pk is not the same as the logged in user not found -- when the user doesnt exist bad request -- when the data is invalid On success: success -- with the updated preferences """ pk, = utils.required_typed_params(kwargs, (int, 'pk')) if not (request.user.pk == pk or request.user.is_superuser): return response.forbidden('You are not allowed to change this users preferences.') preferences = Preferences.objects.get(user=pk) serializer = PreferencesSerializer(preferences, data=request.data, partial=True) if not serializer.is_valid(): return response.bad_request('Invalid preference data provided.') serializer.save() return response.success({'preferences': serializer.data})
def list(self, request): """Get all users and their roles for a given course. Arguments: request -- request data course_id -- course ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when the course does not exist forbidden -- when the user is not in the course forbidden -- when the user is unauthorized to view its participants On success: success -- list of all the users and their role """ course_id, = utils.required_typed_params(request.query_params, (int, 'course_id')) course = Course.objects.get(pk=course_id) request.user.check_permission('can_view_course_users', course) users = UserSerializer(course.users, context={ 'course': course }, many=True).data return response.success({'participants': users})
def partial_update(self, request, *args, **kwargs): """Update an existing course. Arguments: request -- request data data -- the new data for the course pk -- course ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when the course does not exist forbidden -- when the user is not in the course unauthorized -- when the user is unauthorized to edit the course bad_request -- when there is invalid data in the request On success: success -- with the new course data """ course_id, = utils.required_typed_params(kwargs, (int, 'pk')) course = Course.objects.get(pk=course_id) request.user.check_permission('can_edit_course_details', course) data = request.data if 'lti_id' in data: factory.make_lti_ids(lti_id=data['lti_id'], for_model=Lti_ids.COURSE, course=course) serializer = self.serializer_class(course, data=data, partial=True) if not serializer.is_valid(): response.bad_request() serializer.save() return response.success({'course': serializer.data})
def list(self, request): """Get the student submitted journals of one assignment from a course. Arguments: request -- request data course_id -- course ID assignment_id -- assignment ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when the assignment does not exist forbidden -- when the user has no permission to view the journals of the assignment On succes: success -- with journals and stats about the journals """ assignment_id, course_id = utils.required_typed_params( request.query_params, (int, 'assignment_id'), (int, 'course_id')) assignment = Assignment.objects.get(pk=assignment_id) course = Course.objects.get(pk=course_id) request.user.check_permission('can_view_all_journals', assignment) request.user.check_can_view(assignment) users = course.participation_set.filter( role__can_have_journal=True).values('user') queryset = assignment.journal_set.filter(user__in=users) journals = JournalSerializer(queryset, many=True).data return response.success({'journals': journals})
def partial_update(self, request, *args, **kwargs): """Update an existing course group. Arguments: request -- request data name -- group name pk -- group ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when the course does not exists forbidden -- when the user is not in the course unauthorized -- when the user is unauthorized to edit the course bad_request -- when there is invalid data in the request On success: success -- with the new course data """ name, = utils.required_params(request.data, 'name') group_id, = utils.required_typed_params(kwargs, (int, 'pk')) group = Group.objects.get(pk=group_id) course = group.course request.user.check_permission('can_edit_course_user_group', course) if not name: return response.bad_request('Group name is not allowed to be empty.') serializer = GroupSerializer(group, data={'name': name}, partial=True) if not serializer.is_valid(): return response.bad_request() serializer.save() return response.success({'group': serializer.data})
def list(self, request): """Get course roles. Arguments: request -- request data course_id -- course ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when the course does not exist forbidden -- when the user is not in the course forbidden -- when the user is unauthorized to edit its roles On success: success -- list of all the roles in the course """ course_id, = utils.required_typed_params(request.query_params, (int, 'course_id')) course = Course.objects.get(pk=course_id) # TODO: P Is this the right permission request.user.check_permission('can_edit_course_roles', course) roles = Role.objects.filter(course=course) serializer = RoleSerializer(roles, many=True) return response.success({'roles': serializer.data})
def download(self, request, pk): """Get a user file by name if it exists. Arguments: request -- the request that was sent file_name -- filename to download pk -- user ID Returns On failure: unauthorized -- when the user is not logged in bad_request -- when the file was not found forbidden -- when its not a superuser nor their own data On success: success -- a zip file of all the userdata with all their files """ if int(pk) == 0: pk = request.user.id file_name, entry_id, node_id, content_id = utils.required_typed_params( request.query_params, (str, 'file_name'), (int, 'entry_id'), (int, 'node_id'), (int, 'content_id')) try: user_file = UserFile.objects.get(author=pk, file_name=file_name, entry=entry_id, node=node_id, content=content_id) if user_file.author != request.user: request.user.check_permission('can_view_all_journals', user_file.assignment) except (UserFile.DoesNotExist, ValueError): return response.bad_request(file_name + ' was not found.') return response.file(user_file)
def list(self, request): group_id, = utils.required_typed_params(request.query_params, (int, 'group_id')) group = Group.objects.get(pk=group_id) request.user.check_permission('can_edit_course_user_group', group.course) serializer = ParticipationSerializer(Participation.objects.filter(groups=group), many=True) return response.success({'members': serializer.data})
def partial_update(self, request, *args, **kwargs): """Update an existing assignment. Arguments: request -- request data data -- the new data for the assignment pk -- assignment ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when the assignment does not exist forbidden -- User not allowed to edit this assignment unauthorized -- when the user is unauthorized to edit the assignment bad_request -- when there is invalid data in the request On success: success -- with the new assignment data """ # Get data pk, = utils.required_typed_params(kwargs, (int, 'pk')) assignment = Assignment.objects.get(pk=pk) published, = utils.optional_params(request.data, 'published') # Remove data that must not be changed by the serializer req_data = request.data req_data.pop('published', None) if not (request.user.is_superuser or request.user == assignment.author): req_data.pop('author', None) response_data = {} # Publish is possible and asked for if published: request.user.check_permission('can_publish_grades', assignment) self.publish(request, assignment) response_data['published'] = published # Update assignment data is possible and asked for if req_data: if 'lti_id' in req_data: factory.make_lti_ids(lti_id=req_data['lti_id'], for_model=Lti_ids.ASSIGNMENT, assignment=assignment) # If a entry has been submitted to one of the journals of the journal it cannot be unpublished if assignment.is_published and 'is_published' in req_data and not req_data['is_published'] and \ Entry.objects.filter(node__journal__assignment=assignment).exists(): return response.bad_request( 'You are not allowed to unpublish an assignment that already has submissions.') serializer = AssignmentSerializer(assignment, data=req_data, context={'user': request.user}, partial=True) if not serializer.is_valid(): response.bad_request() serializer.save() response_data['assignment'] = serializer.data return response.success(response_data)
def destroy(self, request, pk): user_id, = utils.required_typed_params(request.query_params, (int, 'user_id')) group = Group.objects.get(pk=pk) member = Participation.objects.get(user=user_id, course=group.course) request.user.check_permission('can_edit_course_user_group', group.course) member.groups.remove(group) member.save() return response.success(description='Removed {} from {}.'.format(member.user.full_name, group.name))
def create(self, request): group_id, user_id = utils.required_typed_params(request.data, (int, 'group_id'), (int, 'user_id')) group = Group.objects.get(pk=group_id) member = Participation.objects.get(user=user_id, course=group.course) request.user.check_permission('can_edit_course_user_group', group.course) member.groups.add(group) member.save() users = UserSerializer(group.course.users, context={'course': group.course}, many=True).data return response.created({'participants': users})
def publish_all_assignment_grades(self, request): """This will publish all (unpublished) grades for a given assignment.""" assignment_id, = utils.required_typed_params(request.data, (int, 'assignment_id')) assignment = Assignment.objects.get(pk=assignment_id) request.user.check_permission('can_publish_grades', assignment) grading_tasks.publish_all_assignment_grades(request.user, assignment.pk) return response.success()
def datanose(self, request): """""" course_id, = utils.required_typed_params(request.query_params, (int, 'course_id')) course = Course.objects.get(pk=course_id) check_can_view_groups(request.user, course) factory.make_lti_groups(course) queryset = Group.objects.filter(course=course) serializer = GroupSerializer(queryset, many=True, context={'user': request.user, 'course': course}) return response.success({'groups': serializer.data})
def create(self, request): """Set a new grade for an entry. Arguments: request -- request data entry_id -- entry ID grade -- grade published -- published state Returns: On failure: unauthorized -- when the user is not logged in key_error -- missing keys not_found -- could not find the entry, author or assignment On success: success -- with the assignment data """ entry_id, grade, published = utils.required_typed_params( request.data, (int, 'entry_id'), (float, 'grade'), (bool, 'published')) entry = Entry.objects.get(pk=entry_id) journal = entry.node.journal assignment = journal.assignment request.user.check_permission('can_grade', assignment) if published: request.user.check_permission('can_publish_grades', assignment) if grade is not None and grade < 0: return response.bad_request( 'Grade must be greater than or equal to zero.') grade = factory.make_grade(entry, request.user.pk, grade, published) if published: Comment.objects.filter(entry=entry).update(published=True) # TODO: Is the lti flag ever used? Else move replace_result to celery return response.created({ 'entry': EntrySerializer(entry, context={ 'user': request.user }).data, 'lti': lti_grade.replace_result(journal) })
def partial_update(self, request, *args, **kwargs): """Update an existing journal. Arguments: request -- request data data -- the new data for the journal pk -- journal ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when the journal does not exist forbidden -- User not allowed to edit this journal unauthorized -- when the user is unauthorized to edit the journal bad_request -- when there is invalid data in the request On success: success -- with the new journal data """ pk, = utils.required_typed_params(kwargs, (int, 'pk')) journal = Journal.objects.get(pk=pk) request.user.check_can_view(journal.assignment) req_data = request.data published, = utils.optional_params(request.data, 'published') if published: request.user.check_permission('can_publish_grades', journal.assignment) req_data.pop('published', None) return self.publish(request, journal) bonus_points, = utils.optional_typed_params(request.data, (float, 'bonus_points')) if bonus_points is not None: request.user.check_permission('can_grade', journal.assignment) req_data.pop('bonus_points', None) journal.bonus_points = bonus_points journal.save() lti_grade.replace_result(journal) return response.success( {'journal': JournalSerializer(journal).data}) if not request.user.is_superuser: return response.forbidden( 'You are not allowed to edit this journal.') return self.admin_update(request, journal)
def create(self, request): """Add a user to a course. Arguments: request -- request data user_id -- user ID course_id -- course ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when course or user is not found forbidden -- when the logged in user is not connected to the course bad request -- when the new user is already connected to the course not found -- when the role doesnt exist On success: success -- success message """ user_id, course_id = utils.required_typed_params( request.data, (int, 'user_id'), (int, 'course_id')) role_name = 'Student' user = User.objects.get(pk=user_id) course = Course.objects.get(pk=course_id) request.user.check_permission('can_add_course_users', course) if user.is_participant(course): return response.bad_request( 'User already participates in the course.') role = Role.objects.get(name=role_name, course=course) factory.make_participation(user, course, role) assignments = course.assignment_set.all() for assignment in assignments: if not Journal.objects.filter(assignment=assignment, user=user).exists(): factory.make_journal(assignment, user) serializer = UserSerializer(user, context={'course': course}) return response.created( {'participant': serializer.data}, description='Successfully added student to course.')
def destroy(self, request, *args, **kwargs): """Delete an entry and the node it belongs to. Arguments: request -- request data pk -- entry ID Returns: On failure: not found -- when the course does not exist unauthorized -- when the user is not logged in forbidden -- when the user is not in the course On success: success -- with a message that the course was deleted """ pk, = utils.required_typed_params(kwargs, (int, 'pk')) entry = Entry.objects.get(pk=pk) journal = entry.node.journal assignment = journal.assignment if journal.user == request.user: request.user.check_permission( 'can_have_journal', assignment, 'You are not allowed to delete entries.') if entry.is_graded(): return response.forbidden( 'You are not allowed to delete graded entries.') if entry.is_locked(): return response.forbidden( 'You are not allowed to delete locked entries.') if assignment.is_locked(): return response.forbidden( 'You are not allowed to delete entries in a locked assignment.' ) elif not request.user.is_superuser: return response.forbidden( 'You are not allowed to delete someone else\'s entry.') if journal.needs_lti_link(): return response.forbidden(journal.outdated_link_warning_msg) if entry.node.type != Node.ENTRYDEADLINE: entry.node.delete() entry.delete() return response.success(description='Successfully deleted entry.')
def retrieve(self, request, pk=None): """Retrieve an assignment. Arguments: request -- request data lti -- if this is set, the pk is an lti_id, not a 'normal' id course_id -- get information about that course pk -- assignment ID Returns: On failure: unauthorized -- when the user is not logged in not_found -- could not find the course with the given id forbidden -- not allowed to retrieve assignments in this course On success: success -- with the assignment data """ if 'lti' in request.query_params: assignment = Assignment.objects.get(lti_id_set__contains=[pk]) else: assignment = Assignment.objects.get(pk=pk) try: course_id, = utils.required_typed_params(request.query_params, (int, 'course_id')) course = Course.objects.get(id=course_id) except (VLEMissingRequiredKey, VLEParamWrongType): course = None request.user.check_can_view(assignment) serializer = AssignmentSerializer(assignment, context={ 'user': request.user, 'course': course, 'journals': request.user.has_permission( 'can_grade', assignment) }) return response.success({'assignment': serializer.data})
def partial_update(self, request, pk): """Update user role in a course. Arguments: request -- request data user_id -- user ID role -- name of the role (default: Student) pk -- course ID Returns: On failure: unauthorized -- when the user is not logged in not found -- when the perticipation is not found forbidden -- when the user is not connected to the course forbidden -- when the user is not allowed to change the perticipation On success: success -- with the new role name """ user_id, = utils.required_typed_params(request.data, (int, 'user_id')) role_name, group_names = utils.optional_params(request.data, 'role', 'groups') user = User.objects.get(pk=user_id) course = Course.objects.get(pk=pk) participation = Participation.objects.get(user=user, course=course) request.user.check_permission('can_edit_course_user_group', course) if role_name: request.user.check_permission('can_edit_course_roles', course) participation.role = Role.objects.get(name=role_name, course=course) if group_names: participation.groups.set( Group.objects.get(name=group_names, course=course)) elif 'groups' in request.data: participation.groups.set(None) participation.save() serializer = ParticipationSerializer(participation, context={'course': course}) return response.success( {'participation': serializer.data}, description='Successfully updated participation.')
def published_state(self, request, *args, **kwargs): """Update the grade publish status for whole assignment. Arguments: request -- the request that was send with published -- new published state pk -- assignment ID Returns a json string if it was successful or not. """ assignment_id, = utils.required_typed_params(kwargs, (int, 'pk')) published, = utils.required_params(request.data, 'published') assignment = Assignment.objects.get(pk=assignment_id) request.user.check_permission('can_publish_grades', assignment) self.publish(request, assignment, published) return response.success(payload={'new_published': published})