Example #1
0
def is_reattempting_exam(from_status, to_status):
    """
    Returns a boolean representing whether or not a user is trying to reattempt an exam.

    Given user's exam is in progress, and he is kicked out due to low bandwidth or
    closes the secure browser. Its expected that the user should not be able to restart
    the exam workflow.
    This behavior is being implemented due to edX's integrity constraints.
    """
    return (
        ProctoredExamStudentAttemptStatus.is_in_progress_status(from_status)
        and ProctoredExamStudentAttemptStatus.is_pre_started_status(to_status))
    def handle(self, *args, **options):
        """
        Management command entry point, simply call into the signal firiing
        """
        # pylint: disable=import-outside-toplevel
        from edx_proctoring.api import (update_attempt_status, get_exam_by_id)

        exam_id = options['exam_id']
        user_id = options['user_id']
        to_status = options['to_status']

        msg = (u'Running management command to update user {user_id} '
               u'attempt status on exam_id {exam_id} to {to_status}'.format(
                   user_id=user_id, exam_id=exam_id, to_status=to_status))
        self.stdout.write(msg)

        if not ProctoredExamStudentAttemptStatus.is_valid_status(to_status):
            raise CommandError(
                u'{to_status} is not a valid attempt status!'.format(
                    to_status=to_status))

        # get exam, this will throw exception if does not exist, so let it bomb out
        get_exam_by_id(exam_id)

        update_attempt_status(exam_id, user_id, to_status)

        self.stdout.write('Completed!')
Example #3
0
    def _create_exam_attempt(self,
                             exam_id,
                             status=ProctoredExamStudentAttemptStatus.created):
        """
        Creates the ProctoredExamStudentAttempt object.
        """
        attempt = ProctoredExamStudentAttempt(
            proctored_exam_id=exam_id,
            user_id=self.user_id,
            external_id=self.external_id,
            status=status,
            allowed_time_limit_mins=10,
            taking_as_proctored=True,
        )

        if status in (ProctoredExamStudentAttemptStatus.started,
                      ProctoredExamStudentAttemptStatus.ready_to_submit,
                      ProctoredExamStudentAttemptStatus.submitted):
            attempt.started_at = datetime.now(pytz.UTC)

        if ProctoredExamStudentAttemptStatus.is_completed_status(status):
            attempt.completed_at = datetime.now(pytz.UTC)

        attempt.save()

        return attempt
Example #4
0
    def handle(self, *args, **options):
        """
        Management command entry point, simply call into the signal firiing
        """

        from edx_proctoring.api import (
            update_attempt_status,
            get_exam_by_id
        )

        exam_id = options['exam_id']
        user_id = options['user_id']
        to_status = options['to_status']

        msg = (
            'Running management command to update user {user_id} '
            'attempt status on exam_id {exam_id} to {to_status}'.format(
                user_id=user_id,
                exam_id=exam_id,
                to_status=to_status
            )
        )
        self.stdout.write(msg)

        if not ProctoredExamStudentAttemptStatus.is_valid_status(to_status):
            raise CommandError('{to_status} is not a valid attempt status!'.format(to_status=to_status))

        # get exam, this will throw exception if does not exist, so let it bomb out
        get_exam_by_id(exam_id)

        update_attempt_status(exam_id, user_id, to_status)

        self.stdout.write('Completed!')
Example #5
0
    def _create_exam_attempt(self,
                             exam_id,
                             status=ProctoredExamStudentAttemptStatus.created,
                             is_practice_exam=False,
                             time_remaining_seconds=None):
        """
        Creates the ProctoredExamStudentAttempt object.
        """
        attempt = ProctoredExamStudentAttempt(
            proctored_exam_id=exam_id,
            user_id=self.user_id,
            external_id=self.external_id,
            status=status,
            allowed_time_limit_mins=10,
            taking_as_proctored=True,
            is_sample_attempt=is_practice_exam,
            time_remaining_seconds=time_remaining_seconds,
        )

        if status in (ProctoredExamStudentAttemptStatus.started,
                      ProctoredExamStudentAttemptStatus.ready_to_submit,
                      ProctoredExamStudentAttemptStatus.submitted):
            attempt.started_at = datetime.now(pytz.UTC)

        if ProctoredExamStudentAttemptStatus.is_completed_status(status):
            attempt.completed_at = datetime.now(pytz.UTC)

        if status == ProctoredExamStudentAttemptStatus.error:
            attempt.is_resumable = True

        attempt.save()

        return attempt
Example #6
0
def start_exam_callback(request, attempt_code):  # pylint: disable=unused-argument
    """
    A callback endpoint which is called when SoftwareSecure completes
    the proctoring setup and the exam should be started.

    This is an authenticated endpoint and the attempt_code is passed in
    as part of the URL path

    IMPORTANT: This is an unauthenticated endpoint, so be VERY CAREFUL about extending
    this endpoint
    """
    attempt = get_exam_attempt_by_code(attempt_code)
    if not attempt:
        log.warning(u"Attempt code %r cannot be found.", attempt_code)
        return HttpResponse(
            content='You have entered an exam code that is not valid.',
            status=404
        )
    proctored_exam_id = attempt['proctored_exam']['id']
    attempt_status = attempt['status']
    user_id = attempt['user']['id']
    if attempt_status in [ProctoredExamStudentAttemptStatus.created,
                          ProctoredExamStudentAttemptStatus.download_software_clicked]:
        mark_exam_attempt_as_ready(proctored_exam_id, user_id)

    # if a user attempts to re-enter an exam that has not yet been submitted, submit the exam
    if ProctoredExamStudentAttemptStatus.is_in_progress_status(attempt_status):
        update_attempt_status(proctored_exam_id, user_id, ProctoredExamStudentAttemptStatus.submitted)
    else:
        log.warning(u"Attempted to enter proctored exam attempt {attempt_id} when status was {attempt_status}"
                    .format(
                        attempt_id=attempt['id'],
                        attempt_status=attempt_status,
                    ))

    if switch_is_active(RPNOWV4_WAFFLE_NAME):  # pylint: disable=illegal-waffle-usage
        course_id = attempt['proctored_exam']['course_id']
        content_id = attempt['proctored_exam']['content_id']

        exam_url = ''
        try:
            exam_url = reverse('jump_to', args=[course_id, content_id])
        except NoReverseMatch:
            log.exception(u"BLOCKING ERROR: Can't find course info url for course %s", course_id)
        response = HttpResponseRedirect(exam_url)
        response.set_signed_cookie('exam', attempt['attempt_code'])
        return response

    template = loader.get_template('proctored_exam/proctoring_launch_callback.html')

    return HttpResponse(
        template.render({
            'platform_name': settings.PLATFORM_NAME,
            'link_urls': settings.PROCTORING_SETTINGS.get('LINK_URLS', {})
        })
    )
Example #7
0
def start_exam_callback(request, attempt_code):  # pylint: disable=unused-argument
    """
    A callback endpoint which is called when SoftwareSecure completes
    the proctoring setup and the exam should be started.

    This is an authenticated endpoint and the attempt_code is passed in
    as part of the URL path

    IMPORTANT: This is an unauthenticated endpoint, so be VERY CAREFUL about extending
    this endpoint
    """
    attempt = get_exam_attempt_by_code(attempt_code)
    if not attempt:
        log.warning('attempt_code={attempt_code} cannot be found.'.format(
            attempt_code=attempt_code))
        return HttpResponse(
            content='You have entered an exam code that is not valid.',
            status=404)
    attempt_status = attempt['status']
    if attempt_status in [
            ProctoredExamStudentAttemptStatus.created,
            ProctoredExamStudentAttemptStatus.download_software_clicked
    ]:
        mark_exam_attempt_as_ready(attempt['id'])

    # if a user attempts to re-enter an exam that has not yet been submitted, submit the exam
    if ProctoredExamStudentAttemptStatus.is_in_progress_status(attempt_status):
        update_attempt_status(attempt['id'],
                              ProctoredExamStudentAttemptStatus.submitted)
    else:
        log.warning(
            'Attempted to enter proctored exam attempt_id={attempt_id} when status={attempt_status}'
            .format(
                attempt_id=attempt['id'],
                attempt_status=attempt_status,
            ))

    course_id = attempt['proctored_exam']['course_id']
    content_id = attempt['proctored_exam']['content_id']

    exam_url = ''
    try:
        exam_url = reverse('jump_to', args=[course_id, content_id])
    except NoReverseMatch:
        log.exception(
            "BLOCKING ERROR: Can't find course info url for course_id=%s",
            course_id)
    response = HttpResponseRedirect(exam_url)
    response.set_signed_cookie('exam', attempt['attempt_code'])
    return response
Example #8
0
    def handle(self, *args, **options):
        """
        Management command entry point, simply call into the signal firiing
        """
        # pylint: disable=import-outside-toplevel
        from edx_proctoring.api import update_attempt_status

        attempt_id = options['attempt_id']
        to_status = options['to_status']

        msg = (u'Running management command to update '
               u'attempt {attempt_id} status to {to_status}'.format(
                   attempt_id=attempt_id, to_status=to_status))
        self.stdout.write(msg)

        if not ProctoredExamStudentAttemptStatus.is_valid_status(to_status):
            raise CommandError(
                u'{to_status} is not a valid attempt status!'.format(
                    to_status=to_status))

        update_attempt_status(attempt_id, to_status)

        self.stdout.write('Completed!')
Example #9
0
    def _create_exam_attempt(self, exam_id, status='created'):
        """
        Creates the ProctoredExamStudentAttempt object.
        """

        attempt = ProctoredExamStudentAttempt(
            proctored_exam_id=exam_id,
            user_id=self.user_id,
            external_id=self.external_id,
            allowed_time_limit_mins=10,
            status=status
        )

        if status in (ProctoredExamStudentAttemptStatus.started,
                      ProctoredExamStudentAttemptStatus.ready_to_submit, ProctoredExamStudentAttemptStatus.submitted):
            attempt.started_at = datetime.now(pytz.UTC)

        if ProctoredExamStudentAttemptStatus.is_completed_status(status):
            attempt.completed_at = datetime.now(pytz.UTC)

        attempt.save()

        return attempt