Beispiel #1
0
def get_lti_params(request, *keys):
    jwt_params, = utils.optional_params(request.data, 'jwt_params')
    if jwt_params:
        lti_params = lti.decode_lti_params(jwt_params)
    else:
        lti_params = {'empty': ''}
    values = utils.optional_params(lti_params, *keys)
    values.append(settings.ROLES['Teacher'] in lti_launch.roles_to_list(lti_params))
    return values
Beispiel #2
0
    def create(self, request):
        """Create a new course group.

        Arguments:
        request -- the request that was send with
            name -- name of the course group
            course_id -- course ID of the course
            lti_id -- (optional) lti_id to link the course to

        Returns:
        On failure:
            unauthorized -- when the user is not logged in
            forbidden -- when the user has no permission to create new groups
        On success, with the course group.
        """
        name, course_id = utils.required_params(request.data, "name", "course_id")
        lti_id, = utils.optional_params(request.data, 'lti_id')

        course = Course.objects.get(pk=course_id)

        request.user.check_permission('can_add_course_user_group', course)

        if lti_id and Group.objects.filter(lti_id=lti_id, course=course).exists():
            return response.bad_request('Course group with the desired name already exists.')

        course_group = factory.make_course_group(name, course, lti_id)
        serializer = GroupSerializer(course_group, many=False)
        return response.created({'group': serializer.data})
Beispiel #3
0
def update_lti_course_if_exists(request, user, role):
    """Update a course with lti request.

    If no course exists, return None
    If it does exist:
    1. If the to be processed user is a test student, remove any other test students from the course, 1 max.
    2. Put the user in the course
    3. Add groups to the user
    """
    course_lti_id = request.get('custom_course_id', None)
    course = Course.objects.filter(active_lti_id=course_lti_id)
    if course_lti_id is None or not course.exists():
        return None

    # There can only be one actively linked course.
    course = course.first()

    # Can only have one active test student per course at a time.
    if user.is_test_student:
        User.objects.filter(participation__course=course,
                            is_test_student=True).exclude(pk=user.pk).delete()

    # If the user not is a participant, add participation with possibly the role given by the LTI instance.
    if not user.is_participant(course):
        participation = _make_lti_participation(user, course, role)
    else:
        participation = Participation.objects.get(course=course, user=user)

    group_ids, = utils.optional_params(request, 'custom_section_id')
    if group_ids:
        add_groups_if_not_exists(participation, group_ids.split(','))

    return course
Beispiel #4
0
    def create(self, request):
        """Create a new course.

        Arguments:
        request -- request data
            name -- name of the course
            abbreviation -- abbreviation of the course
            startdate -- (optional) date when the course starts
            enddate -- (optional) date when the course ends
            lti_id -- (optional) lti_id to link the course to

        Returns:
        On failure:
            unauthorized -- when the user is not logged in
            forbidden -- when the user has no permission to create new courses
        On succes:
            success -- with the course data
        """
        request.user.check_permission('can_add_course')

        name, abbr = utils.required_params(request.data, 'name',
                                           'abbreviation')
        startdate, enddate, lti_id = utils.optional_params(
            request.data, 'startdate', 'enddate', 'lti_id')

        course = factory.make_course(name, abbr, startdate, enddate,
                                     request.user, lti_id)

        serializer = self.serializer_class(course, many=False)
        return response.created({'course': serializer.data})
Beispiel #5
0
def forgot_password(request):
    """Handles a forgot password request.

    Arguments:
        username -- User claimed username.
        email -- User claimed email.
        token -- Django stateless token, invalidated after password change or after a set time (by default three days).

    Generates a recovery token if a matching user can be found by either the prodived username or email.
    """
    username, email = utils.optional_params(request.data, 'username', 'email')

    # We are retrieving the username based on either the username or email
    try:
        user = User.objects.get(username=username)
        email = 'your recovery address'
    except User.DoesNotExist:
        if email is None or email == '':
            return response.not_found('Invalid email address provided.')
        user = User.objects.get(email=email)

    if not user.email:
        return response.bad_request(
            description='The provided account has no known email address.')

    send_password_recovery_link.delay(user.pk)
    return response.success(
        description='An email was sent to {}, please check your inbox for further instructions.'.format(email))
Beispiel #6
0
    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)
Beispiel #7
0
    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)
Beispiel #8
0
    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_params(request.data, 'user_id')
        role_name, group_name = utils.optional_params(request.data, 'role',
                                                      'group')

        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_name:
            participation.group = Group.objects.get(name=group_name,
                                                    course=course)
        elif 'group' in request.data:
            participation.group = None

        participation.save()
        serializer = UserSerializer(participation.user,
                                    context={'course': course})
        return response.success(
            {'user': serializer.data},
            description='Successfully updated participation.')
Beispiel #9
0
    def create(self, request):
        """Create a new assignment.

        Arguments:
        request -- request data
            name -- name of the assignment
            description -- description of the assignment
            course_id -- id of the course the assignment belongs to
            points_possible -- the possible amount of points for the assignment
            unlock_date -- (optional) date the assignment becomes available on
            due_date -- (optional) date the assignment is due for
            lock_date -- (optional) date the assignment becomes unavailable on
            lti_id -- id labeled link to LTI instance

        Returns:
        On failure:
            unauthorized -- when the user is not logged in
            not_found -- could not find the course with the given id
            key_error -- missing keys
            forbidden -- the user is not allowed to create assignments in this course

        On success:
            succes -- with the assignment data

        """
        name, description, course_id = utils.required_params(request.data, "name", "description", "course_id")
        points_possible, unlock_date, due_date, lock_date, lti_id, is_published = \
            utils.optional_params(request.data, "points_possible", "unlock_date", "due_date", "lock_date", "lti_id",
                                  "is_published")
        course = Course.objects.get(pk=course_id)

        request.user.check_permission('can_add_assignment', course)

        assignment = factory.make_assignment(name, description, courses=[course],
                                             author=request.user, lti_id=lti_id,
                                             points_possible=points_possible,
                                             unlock_date=unlock_date, due_date=due_date,
                                             lock_date=lock_date, is_published=is_published)

        for user in course.users.all():
            factory.make_journal(assignment, user)

        serializer = AssignmentSerializer(assignment, context={'user': request.user, 'course': course})
        return response.created({'assignment': serializer.data})
Beispiel #10
0
    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)

        published, = utils.optional_params(request.data, 'published')
        if published:
            request.user.check_permission('can_publish_grades',
                                          journal.assignment)
            return self.publish(request, journal)

        if not request.user.is_superuser:
            return response.forbidden(
                'You are not allowed to edit this journal.')

        req_data = request.data
        if 'published' in req_data:
            del req_data['published']
        serializer = JournalSerializer(journal, data=req_data, partial=True)
        if not serializer.is_valid():
            response.bad_request()
        serializer.save()

        return response.success({'journal': serializer.data})
Beispiel #11
0
def request_email_verification(request):
    """Request an email with a verifcation link for the user's email address."""
    if request.user.verified_email:
        return response.success(description='Email address already verified.')

    email, = utils.optional_params(request.data, 'email')

    if not email and not request.user.email:
        return response.bad_request(description='Please provide an email address.')

    if email and request.user.email != email:
        request.user.email = email
        request.user.verified_email = False
        request.user.save()

    send_email_verification_link.delay(request.user.pk)
    return response.success(
        description='An email was sent to {}, please check your inbox for further \
                     instructions.'.format(request.user.email))
Beispiel #12
0
def check_course_lti(request, user, role):
    """Check if a course with the lti_id exists.

    If it does, put the user in the group with the right group and role."""
    course_id = request['custom_course_id']
    lti_couple = Lti_ids.objects.filter(lti_id=course_id, for_model=Lti_ids.COURSE).first()

    if not lti_couple:
        return None

    course = lti_couple.course
    lti_id, = utils.optional_params(request, 'custom_section_id')
    # If the user is participatant, but not yet in a group, put the user in the Canvas related group.
    if user.is_participant(course):
        participation = Participation.objects.get(course=course, user=user)
        if not participation.group and lti_id:
            groups = Group.objects.filter(lti_id=lti_id, course=course)
            if groups.exists():
                participation.group = groups[0]
            else:
                group = factory.make_course_group(lti_id, course, lti_id)
                participation.group = group

            participation.save()
        return course

    participation = None
    for r in settings.ROLES:
        if r in role:
            participation = factory.make_participation(user, course, Role.objects.get(name=r, course=course))
            break
    if not participation:
        participation = factory.make_participation(user, course, Role.objects.get(name='Student', course=course))

    groups = Group.objects.filter(lti_id=lti_id, course=course)
    if groups.exists():
        participation.group = groups[0]
    else:
        group = factory.make_course_group(lti_id, course, lti_id)
        participation.group = group
    participation.save()
    return course
Beispiel #13
0
def test_rest(obj, url, create_params=None, delete_params=None, update_params=None, get_params=None,
              get_is_create=True, user=None, password=userfactory.DEFAULT_PASSWORD,
              create_status=201, get_status=200, delete_status=200, get_status_when_unauthorized=401):
    # Create the object that is given
    create_object = create(obj, url, params=create_params, user=user, password=password, status=create_status)
    if create_status != 201:
        return
    create_object.pop('description', None)
    pk, = utils.required_typed_params(list(create_object.values())[0], (int, 'id'))

    # Get that same object
    if get_params is None:
        get_params = dict()
    get(obj, url, params={'pk': pk, **get_params}, status=get_status_when_unauthorized)
    get_object = get(obj, url, params={'pk': pk, **get_params}, user=user, password=password, status=get_status)
    get_object.pop('description', None)

    # Check if the created object is the same as the one it got
    if get_is_create and get_status == 200:
        assert create_object == get_object, 'Created object does not equal the get result.'

    # Update the object
    if update_params is not None:
        if not isinstance(update_params, list):
            update_params = [update_params]

        for to_update in update_params:
            changes, status = utils.optional_params(to_update, 'changes', 'status')
            if changes is None and status is None:
                changes = to_update
            if status is None:
                status = 200
            changes['pk'] = pk
            update_object = update(obj, url, params=changes, user=user, password=password, status=status)
            update_object.pop('description', None)

    # Delete the object
    if delete_params is None:
        delete_params = dict()
    delete(obj, url, params={'pk': pk, **delete_params}, user=user, password=password, status=delete_status)
Beispiel #14
0
def forgot_password(request):
    """Handles a forgot password request.

    Arguments:
        username -- User claimed username.
        email -- User claimed email.
        token -- Django stateless token, invalidated after password change or after a set time (by default three days).

    Generates a recovery token if a matching user can be found by either the prodived username or email.
    """
    username, email = utils.optional_params(request.data, 'username', 'email')

    # We are retrieving the username based on either the username or email
    try:
        user = User.objects.get(username=username)
        email = 'your recovery address'
    except User.DoesNotExist:
        user = User.objects.get(email=email)

    email_handling.send_password_recovery_link(user)
    return response.success(
        description=
        'An email was sent to {}, please follow the email for instructions.'.
        format(email))
Beispiel #15
0
    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

        """
        pk, = utils.required_typed_params(kwargs, (int, 'pk'))
        assignment = Assignment.objects.get(pk=pk)

        request.user.check_permission('can_edit_assignment', assignment)

        response_data = {}

        # Remove data that must not be changed by the serializer
        req_data = request.data
        if not (request.user.is_superuser
                or request.user == assignment.author):
            req_data.pop('author', None)

        # Check if the assignment can be unpublished
        is_published, = utils.optional_params(request.data, 'is_published')
        if not assignment.can_unpublish() and is_published is False:
            return response.bad_request(
                'You cannot unpublish an assignment that already has submissions.'
            )

        active_lti_course, = utils.optional_typed_params(
            request.data, (int, 'active_lti_course'))
        if active_lti_course is not None:
            course = Course.objects.get(pk=active_lti_course)
            active_lti_id = assignment.get_course_lti_id(course)
            if active_lti_id:
                assignment.active_lti_id = active_lti_id
                assignment.save()

        # Rename lti id key for serializer
        if 'lti_id' in req_data:
            course_id, = utils.required_params(request.data, 'course_id')
            course = Course.objects.get(pk=course_id)
            request.user.check_permission('can_add_assignment', course)
            if course in assignment.courses.all():
                return response.bad_request(
                    'Assignment already used in course.')
            course.set_assignment_lti_id_set(req_data['lti_id'])
            course.save()
            assignment.courses.add(course)
            assignment.save()
            for user in User.objects.filter(
                    participation__course=course).exclude(
                        journal__assignment=assignment):
                factory.make_journal(assignment, user)
            req_data['active_lti_id'] = req_data.pop('lti_id')

        # Update the other data
        serializer = AssignmentSerializer(assignment,
                                          data=req_data,
                                          context={'user': request.user},
                                          partial=True)
        if not serializer.is_valid():
            return response.bad_request()
        serializer.save()
        response_data['assignment'] = serializer.data

        return response.success(response_data)
Beispiel #16
0
    def create(self, request):
        """Create a new entry.

        Deletes remaining temporary user files if successful.

        Arguments:
        request -- the request that was send with
            journal_id -- the journal id
            template_id -- the template id to create the entry with
            node_id -- optional: the node to bind the entry to (only for entrydeadlines)
            content -- the list of {tag, data} tuples to bind data to a template field.
        """
        journal_id, template_id, content_list = utils.required_params(
            request.data, "journal_id", "template_id", "content")
        node_id, = utils.optional_params(request.data, "node_id")

        journal = Journal.objects.get(pk=journal_id, user=request.user)
        assignment = journal.assignment
        template = Template.objects.get(pk=template_id)

        request.user.check_permission('can_have_journal', assignment)

        if assignment.is_locked():
            return response.forbidden(
                'The assignment is locked, entries can no longer be edited/changed.'
            )

        if journal.needs_lti_link():
            return response.forbidden(journal.outdated_link_warning_msg)

        # Check if the template is available
        if not (node_id or assignment.format.template_set.filter(
                archived=False, preset_only=False, pk=template.pk).exists()):
            return response.forbidden('Entry template is not available.')

        entry_utils.check_fields(template, content_list)

        # Node specific entry
        if node_id:
            node = Node.objects.get(pk=node_id, journal=journal)
            entry = entry_utils.add_entry_to_node(node, template)
        # Template specific entry
        else:
            entry = factory.make_entry(template)
            node = factory.make_node(journal, entry)

        for content in content_list:
            field_id, = utils.required_typed_params(content, (int, 'id'))
            data, = utils.required_params(content, 'data')
            field = Field.objects.get(pk=field_id)

            created_content = factory.make_content(node.entry, data, field)

            if field.type in field.FILE_TYPES:  # Image, file or PDF
                user_file = file_handling.get_temp_user_file(
                    request.user, assignment, content['data'])
                if user_file is None and field.required:
                    node.entry.delete()
                    # If there is a newly created node, delete that as well
                    if not node_id:
                        node.delete()
                    return response.bad_request(
                        'One of your files was not correctly uploaded, please try again.'
                    )
                elif user_file:
                    file_handling.make_permanent_file_content(
                        user_file, created_content, node)

        # Notify teacher on new entry
        if (node.journal.sourcedid
                and node.entry.vle_coupling == Entry.NEED_SUBMISSION):
            lti_tasks.needs_grading.delay(node.pk)

        # Delete old user files
        file_handling.remove_temp_user_files(request.user)

        return response.created({
            'added':
            entry_utils.get_node_index(journal, node, request.user),
            'nodes':
            timeline.get_nodes(journal, request.user),
            'entry':
            serialize.EntrySerializer(entry, context={
                'user': request.user
            }).data
        })
Beispiel #17
0
    def create(self, request):
        """Create a new user.

        Arguments:
        request -- request data
            username -- username
            password -- password
            first_name -- (optinal) first name
            last_name -- (optinal) last name
            email -- (optinal) email
            jwt_params -- (optinal) jwt params to get the lti information from
                user_id -- id of the user
                user_image -- user image
                roles -- role of the user

        Returns:
        On failure:
            unauthorized -- when the user is not logged in
            bad request -- when email/username/lti id already exists
            bad request -- when email/password is invalid
        On succes:
            success -- with the newly created user data
        """

        lti_id, user_image, full_name, email, is_teacher = get_lti_params(
            request, 'user_id', 'custom_user_image', 'custom_user_full_name', 'custom_user_email')
        if full_name:
            first_name, last_name = lti.split_fullname(full_name)

        if lti_id is None:
            # Check if instance allows standalone registration if user did not register through some LTI instance
            try:
                instance = Instance.objects.get(pk=1)
                if not instance.allow_standalone_registration:
                    return response.bad_request(('{} does not allow you to register through the website,' +
                                                ' please use an LTI instance.').format(instance.name))
            except Instance.DoesNotExist:
                pass

            first_name, last_name, email = utils.optional_params(request.data, 'first_name', 'last_name', 'email')

        username, password = utils.required_params(request.data, 'username', 'password')

        if email and User.objects.filter(email=email).exists():
            return response.bad_request('User with this email already exists.')

        validate_email(email)

        if User.objects.filter(username=username).exists():
            return response.bad_request('User with this username already exists.')

        if lti_id is not None and User.objects.filter(lti_id=lti_id).exists():
            return response.bad_request('User with this lti id already exists.')

        validators.validate_password(password)

        user = factory.make_user(username, password, email=email, lti_id=lti_id, is_teacher=is_teacher,
                                 first_name=first_name, last_name=last_name, profile_picture=user_image,
                                 verified_email=True if lti_id else False)

        if lti_id is None:
            try:
                email_handling.send_email_verification_link(user)
            except SMTPAuthenticationError as err:
                user.delete()
                raise err

        return response.created({'user': UserSerializer(user).data})
Beispiel #18
0
    def partial_update(self, request, *args, **kwargs):
        """Update an existing user.

        Arguments:
        request -- request data
            jwt_params -- jwt params to get the lti information from
                user_id -- id of the user
                user_image -- user image
                roles -- role of the user
        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 exists
            bad request -- when the data is invalid
        On success:
            success -- with the updated user
        """
        pk, = utils.required_typed_params(kwargs, (int, 'pk'))
        if pk == 0:
            pk = request.user.pk
        if not (request.user.pk == pk or request.user.is_superuser):
            return response.forbidden()

        user = User.objects.get(pk=pk)

        lti_id, user_email, user_full_name, user_image, is_teacher = get_lti_params(
            request, 'user_id', 'custom_user_email', 'custom_user_full_name', 'custom_user_image')

        if user_image is not None:
            user.profile_picture = user_image
        if user_email is not None:
            user.email = user_email
            user.verified_email = True
        if user_full_name is not None:
            user.first_name, user.last_name = lti.split_fullname(user_full_name)
        if is_teacher:
            user.is_teacher = is_teacher

        if lti_id is not None:
            if User.objects.filter(lti_id=lti_id).exists():
                return response.bad_request('User with this lti id already exists.')
            user.lti_id = lti_id

        user.save()
        if user.lti_id is not None:
            gn, cn, pp = utils.optional_params(
                request.data, 'grade_notifications', 'comment_notifications', 'profile_picture')
            data = {
                'grade_notifications': gn if gn else user.grade_notifications,
                'comment_notifications': cn if cn else user.comment_notifications,
                'profile_picture': pp if pp else user.profile_picture
            }
        else:
            data = request.data
        serializer = OwnUserSerializer(user, data=data, partial=True)
        if not serializer.is_valid():
            return response.bad_request()
        serializer.save()
        return response.success({'user': serializer.data})
Beispiel #19
0
    def partial_update(self, request, pk):
        """Update an existing journal format.

        Arguments:
        request -- request data
            templates -- the list of templates to bind to the format
            presets -- the list of presets to bind to the format
            removed_presets -- presets to be removed
            removed_templates -- templates to be removed
        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

        """
        assignment_details, templates, presets, removed_templates, removed_presets \
            = utils.required_params(request.data, 'assignment_details', 'templates', 'presets',
                                    'removed_templates', 'removed_presets')

        assignment = Assignment.objects.get(pk=pk)
        format = assignment.format

        request.user.check_permission('can_edit_assignment', assignment)

        # Check if the assignment can be unpublished
        is_published, = utils.optional_params(assignment_details, 'is_published')
        if not assignment.can_unpublish() and is_published is False:
            return response.bad_request("You cannot unpublish an assignment that already has submissions.")

        # Remove data that must not be changed by the serializer
        req_data = assignment_details or {}
        req_data.pop('published', None)
        req_data.pop('author', None)

        for key in req_data:
            if req_data[key] == '':
                req_data[key] = None

        # Update the assignment details
        serializer = AssignmentDetailsSerializer(assignment, data=req_data, context={'user': request.user},
                                                 partial=True)
        if not serializer.is_valid():
            return response.bad_request('Invalid data.')
        serializer.save()

        new_ids = utils.update_templates(format, templates)
        utils.update_presets(assignment, presets, new_ids)

        utils.delete_presets(removed_presets)
        utils.archive_templates(removed_templates)

        serializer = FormatSerializer(format)
        assignment_details = AssignmentDetailsSerializer(assignment, context={'user': request.user})

        return response.success({'format': serializer.data, 'assignment_details': assignment_details.data})
Beispiel #20
0
    def partial_update(self, request, *args, **kwargs):
        """Update an existing user.

        Arguments:
        request -- request data
            jwt_params -- jwt params to get the lti information from
                user_id -- id of the user
                user_image -- user image
                roles -- role of the user
        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 exists
            bad request -- when the data is invalid
        On success:
            success -- with the updated user
        """
        pk, = utils.required_typed_params(kwargs, (int, 'pk'))
        if pk == 0:
            pk = request.user.pk
        if not (request.user.pk == pk or request.user.is_superuser):
            return response.forbidden()

        user = User.objects.get(pk=pk)

        lti_id, user_email, user_full_name, user_image, is_teacher = get_lti_params(
            request, 'user_id', 'custom_user_email', 'custom_user_full_name',
            'custom_user_image')

        if user_image is not None:
            user.profile_picture = user_image
        if user_email:
            if User.objects.filter(email=user_email).exclude(
                    pk=user.pk).exists():
                return response.bad_request(
                    '{} is taken by another account. Link to that account or contact support.'
                    .format(user_email))

            user.email = user_email
            user.verified_email = True
        if user_full_name is not None:
            user.full_name = user_full_name
        if is_teacher:
            user.is_teacher = is_teacher

        if lti_id is not None:
            if User.objects.filter(lti_id=lti_id).exists():
                return response.bad_request(
                    'User with this lti id already exists.')
            elif (bool(lti_id) and not bool(user_email)
                  and user_full_name == settings.LTI_TEST_STUDENT_FULL_NAME
                  or user.is_test_student):
                return response.forbidden(
                    'You are not allowed to link a test account to an existing account.'
                )
            user.lti_id = lti_id

        user.save()
        if user.lti_id is not None:
            pp, = utils.optional_params(request.data, 'profile_picture')
            data = {'profile_picture': pp if pp else user.profile_picture}
        else:
            data = request.data
        serializer = OwnUserSerializer(user, data=data, partial=True)
        if not serializer.is_valid():
            return response.bad_request()
        serializer.save()
        return response.success({'user': serializer.data})