Ejemplo n.º 1
0
    def get(self, request, attempt_id):
        """
        HTTP GET Handler. Returns the status of the exam attempt.
        """
        try:
            attempt_id = int(attempt_id)
            attempt = get_exam_attempt_by_id(attempt_id)
        except:
            attempt = get_exam_attempt_by_code(attempt_id)
        attempt = get_exam_attempt_by_code(attempt_id)

        try:

            if not attempt:
                err_msg = ('Attempted to access attempt_id {attempt_id} but '
                           'it does not exist.'.format(attempt_id=attempt_id))
                return Response(status=status.HTTP_400_BAD_REQUEST)

            # make sure the the attempt belongs to the calling user_id
            if attempt['user']['id'] != request.user.id:
                err_msg = ('Attempted to access attempt_id {attempt_id} but '
                           'does not have access to it.'.format(
                               attempt_id=attempt_id))
                raise ProctoredExamPermissionDenied(err_msg)

            # add in the computed time remaining as a helper
            time_remaining_seconds = get_time_remaining_for_attempt(attempt)

            attempt['time_remaining_seconds'] = time_remaining_seconds

            accessibility_time_string = _(
                'you have {remaining_time} remaining').format(
                    remaining_time=humanized_time(
                        int(round(time_remaining_seconds / 60.0, 0))))

            # special case if we are less than a minute, since we don't produce
            # text translations of granularity at the seconds range
            if time_remaining_seconds < 60:
                accessibility_time_string = _(
                    'you have less than a minute remaining')

            attempt['accessibility_time_string'] = accessibility_time_string

            return Response(data=attempt, status=status.HTTP_200_OK)

        except ProctoredBaseException, ex:
            LOG.exception(ex)
            return Response(status=status.HTTP_400_BAD_REQUEST,
                            data={"detail": str(ex)})
Ejemplo n.º 2
0
    def get(self, request, attempt_code):  # pylint: disable=unused-argument
        """
        Returns the status of an exam attempt. Given that this is an unauthenticated
        caller, we will only return the status string, no additional information
        about the exam
        """

        attempt = get_exam_attempt_by_code(attempt_code)
        ip_address = get_ip(request)
        timestamp = datetime.now(pytz.UTC)
        if not attempt:
            return HttpResponse(
                content='You have entered an exam code that is not valid.',
                status=404
            )

        update_exam_attempt(attempt['id'], last_poll_timestamp=timestamp, last_poll_ipaddr=ip_address)

        return Response(
            data={
                # IMPORTANT: Don't add more information to this as it is an
                # unauthenticated endpoint
                'status': attempt['status'],
            },
            status=200
        )
Ejemplo n.º 3
0
    def get(self, request, attempt_code):  # pylint: disable=unused-argument
        """
        Returns the status of an exam attempt. Given that this is an unauthenticated
        caller, we will only return the status string, no additional information
        about the exam
        """

        attempt = get_exam_attempt_by_code(attempt_code)
        ip_address = get_ip(request)
        timestamp = datetime.now(pytz.UTC)
        if not attempt:
            return HttpResponse(
                content='You have entered an exam code that is not valid.',
                status=404)

        update_exam_attempt(attempt['id'],
                            last_poll_timestamp=timestamp,
                            last_poll_ipaddr=ip_address)

        return Response(
            data={
                # IMPORTANT: Don't add more information to this as it is an
                # unauthenticated endpoint
                'status': attempt['status'],
            },
            status=200)
Ejemplo n.º 4
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', {})
        })
    )
Ejemplo n.º 5
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 %r cannot be found.", attempt_code)
        return HttpResponse(
            content='You have entered an exam code that is not valid.',
            status=404
        )

    if attempt['status'] in [ProctoredExamStudentAttemptStatus.created,
                             ProctoredExamStudentAttemptStatus.download_software_clicked]:
        mark_exam_attempt_as_ready(attempt['proctored_exam']['id'], attempt['user']['id'])
    else:
        log.warning("Attempted to enter proctored exam attempt {attempt_id} when status was {attempt_status}"
                    .format(
                        attempt_id=attempt['id'],
                        attempt_status=attempt['status'],
                    ))

    log.info("Exam %r has been marked as ready", attempt['proctored_exam']['id'])
    if switch_is_active(RPNOWV4_WAFFLE_NAME):
        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 %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', {})
        })
    )
Ejemplo n.º 6
0
    def put(self, request, attempt_id):
        """
        HTTP POST handler. To stop an exam.
        """
        try:
            attempt_id = int(attempt_id)
            attempt = get_exam_attempt_by_id(attempt_id)
        except:
            attempt = get_exam_attempt_by_code(attempt_id)

        try:
            if not attempt:
                err_msg = ('Attempted to access attempt_id {attempt_id} but '
                           'it does not exist.'.format(attempt_id=attempt_id))
                raise StudentExamAttemptDoesNotExistsException(err_msg)

            # make sure the the attempt belongs to the calling user_id
            if attempt['user']['id'] != request.user.id:
                err_msg = ('Attempted to access attempt_id {attempt_id} but '
                           'does not have access to it.'.format(
                               attempt_id=attempt_id))
                raise ProctoredExamPermissionDenied(err_msg)

            action = request.data.get('action')

            if action == 'stop':
                exam_attempt_id = stop_exam_attempt(
                    exam_id=attempt['proctored_exam']['id'],
                    user_id=request.user.id)
            elif action == 'start':
                exam_attempt_id = start_exam_attempt(
                    exam_id=attempt['proctored_exam']['id'],
                    user_id=request.user.id)
            elif action == 'submit':
                exam_attempt_id = update_attempt_status(
                    attempt['proctored_exam']['id'], request.user.id,
                    ProctoredExamStudentAttemptStatus.submitted)
            elif action == 'click_download_software':
                exam_attempt_id = update_attempt_status(
                    attempt['proctored_exam']['id'], request.user.id,
                    ProctoredExamStudentAttemptStatus.download_software_clicked
                )
            elif action == 'decline':
                exam_attempt_id = update_attempt_status(
                    attempt['proctored_exam']['id'], request.user.id,
                    ProctoredExamStudentAttemptStatus.declined)
            return Response({"exam_attempt_id": exam_attempt_id})

        except ProctoredBaseException, ex:
            LOG.exception(ex)
            return Response(status=status.HTTP_400_BAD_REQUEST,
                            data={"detail": str(ex)})
Ejemplo n.º 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
Ejemplo n.º 8
0
    def put(self, request, attempt_code):
        """
        HTTP POST handler. To stop an exam.
        """
        try:
            attempt = get_exam_attempt_by_code(attempt_code)

            if not attempt:
                err_msg = (
                    'Attempted to access attempt_code {attempt_code} but '
                    'it does not exist.'.format(
                        attempt_code=attempt_code
                    )
                )
                raise StudentExamAttemptDoesNotExistsException(err_msg)

            action = request.DATA.get('action')
            user_id = request.DATA.get('user_id')
            exam_id = attempt['proctored_exam']['id']

            if action and action == 'submit':
                exam_attempt_id = update_attempt_status(
                    exam_id,
                    user_id,
                    ProctoredExamStudentAttemptStatus.submitted
                )
                
            if action and action == 'fail':
                exam_attempt_id = update_attempt_status(
                    exam_id,
                    user_id,
                    ProctoredExamStudentAttemptStatus.error
                )
                
            if action and action == 'decline':
                exam_attempt_id = update_attempt_status(
                    exam_id,
                    user_id,
                    ProctoredExamStudentAttemptStatus.timed_out
                )

            return Response({"exam_attempt_id": exam_attempt_id})

        except ProctoredBaseException, ex:
            LOG.exception(ex)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"detail": str(ex)}
            )
Ejemplo n.º 9
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.

    NOTE: This returns HTML as it will be displayed in an embedded browser

    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:
        return HttpResponse(
            content='You have entered an exam code that is not valid.',
            status=404
        )

    if attempt['status'] in [ProctoredExamStudentAttemptStatus.created,
                             ProctoredExamStudentAttemptStatus.download_software_clicked]:
        mark_exam_attempt_as_ready(attempt['proctored_exam']['id'], attempt['user']['id'])

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

    poll_url = reverse(
        'edx_proctoring.anonymous.proctoring_poll_status',
        args=[attempt_code]
    )

    return HttpResponse(
        template.render(
            Context({
                'exam_attempt_status_url': poll_url,
                'platform_name': settings.PLATFORM_NAME,
                'link_urls': settings.PROCTORING_SETTINGS.get('LINK_URLS', {})
            })
        )
    )
Ejemplo n.º 10
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.

    NOTE: This returns HTML as it will be displayed in an embedded browser

    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.warn("Attempt code %r cannot be found.", attempt_code)
        return HttpResponse(
            content='You have entered an exam code that is not valid.',
            status=404)

    if attempt['status'] in [
            ProctoredExamStudentAttemptStatus.created,
            ProctoredExamStudentAttemptStatus.download_software_clicked
    ]:
        mark_exam_attempt_as_ready(attempt['proctored_exam']['id'],
                                   attempt['user']['id'])

    log.info("Exam %r has been marked as ready",
             attempt['proctored_exam']['id'])
    template = loader.get_template(
        'proctored_exam/proctoring_launch_callback.html')

    return HttpResponse(
        template.render(
            Context({
                'platform_name':
                settings.PLATFORM_NAME,
                'link_urls':
                settings.PROCTORING_SETTINGS.get('LINK_URLS', {})
            })))
Ejemplo n.º 11
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.

    NOTE: This returns HTML as it will be displayed in an embedded browser

    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:
        return HttpResponse(
            content='You have entered an exam code that is not valid.',
            status=404)

    mark_exam_attempt_as_ready(attempt['proctored_exam']['id'],
                               attempt['user']['id'])

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

    poll_url = reverse('edx_proctoring.anonymous.proctoring_poll_status',
                       args=[attempt_code])

    provider_name = get_provider_name_by_course_id(
        attempt['proctored_exam']['course_id'])
    proctoring_settings = get_proctoring_settings(provider_name)
    return HttpResponse(
        template.render(
            Context({
                'exam_attempt_status_url': poll_url,
                'platform_name': settings.PLATFORM_NAME,
                'link_urls': proctoring_settings.get('LINK_URLS', {})
            })))
Ejemplo n.º 12
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.
    NOTE: This returns HTML as it will be displayed in an embedded browser
    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.warn("Attempt code %r cannot be found.", attempt_code)
        return HttpResponse(
            content='You have entered an exam code that is not valid.',
            status=404
        )

    if attempt['status'] in [ProctoredExamStudentAttemptStatus.created,
                             ProctoredExamStudentAttemptStatus.download_software_clicked]:
        mark_exam_attempt_as_ready(attempt['proctored_exam']['id'], attempt['user']['id'])

    #log.info("Exam %r has been marked as ready", attempt['proctored_exam']['id'])
    #template = loader.get_template('proctored_exam/proctoring_launch_callback.html')

    #return HttpResponse(
    #    template.render(
    #        Context({
    #            'platform_name': settings.PLATFORM_NAME,
    #            'link_urls': get_proctoring_settings(attempt['provider_name']).get('LINK_URLS', {})
    #        })
    #    )
        #)
    scheme = 'https' if getattr(settings, 'HTTPS', 'on') == 'on' else 'http'
    hostname = settings.SITE_NAME
    path=reverse('jump_to', kwargs={'course_id': attempt['proctored_exam']['course_id'], 'location': attempt['proctored_exam']['content_id']})
    return redirect(path)
Ejemplo n.º 13
0
    def get(self, request, attempt_code):  # pylint: disable=unused-argument
        """
        Returns the status of an exam attempt. Given that this is an unauthenticated
        caller, we will only return the status string, no additional information
        about the exam
        """

        attempt = get_exam_attempt_by_code(attempt_code)
        if not attempt:
            return HttpResponse(
                content='You have entered an exam code that is not valid.',
                status=404
            )

        log.info("{} {}".format(attempt_code, attempt['status']))
        return Response(
            data={
                # IMPORTANT: Don't add more information to this as it is an
                # unauthenticated endpoint
                'status': attempt['status'],
            },
            status=200
        )
Ejemplo n.º 14
0
    def put(self, request, attempt_code):
        """
        HTTP POST handler. To stop an exam.
        """
        try:
            attempt = get_exam_attempt_by_code(attempt_code)

            if not attempt:
                err_msg = (
                    'Attempted to access attempt_code {attempt_code} but '
                    'it does not exist.'.format(attempt_code=attempt_code))
                raise StudentExamAttemptDoesNotExistsException(err_msg)

            action = request.DATA.get('action')
            user_id = request.DATA.get('user_id')
            exam_id = attempt['proctored_exam']['id']

            if action and action == 'submit':
                exam_attempt_id = update_attempt_status(
                    exam_id, user_id,
                    ProctoredExamStudentAttemptStatus.submitted)

            if action and action == 'fail':
                exam_attempt_id = update_attempt_status(
                    exam_id, user_id, ProctoredExamStudentAttemptStatus.error)

            if action and action == 'decline':
                exam_attempt_id = update_attempt_status(
                    exam_id, user_id,
                    ProctoredExamStudentAttemptStatus.timed_out)

            return Response({"exam_attempt_id": exam_attempt_id})

        except ProctoredBaseException, ex:
            LOG.exception(ex)
            return Response(status=status.HTTP_400_BAD_REQUEST,
                            data={"detail": str(ex)})
Ejemplo n.º 15
0
    def delete(self, request, attempt_id):  # pylint: disable=unused-argument
        """
        HTTP DELETE handler. Removes an exam attempt.
        """

        try:
            attempt_id = int(attempt_id)
            attempt = get_exam_attempt_by_id(attempt_id)
        except:
            attempt = get_exam_attempt_by_code(attempt_id)

        try:
            if not attempt:
                err_msg = ('Attempted to access attempt_id {attempt_id} but '
                           'it does not exist.'.format(attempt_id=attempt_id))
                raise StudentExamAttemptDoesNotExistsException(err_msg)

            remove_exam_attempt(attempt_id, request.user)
            return Response()

        except ProctoredBaseException, ex:
            LOG.exception(ex)
            return Response(status=status.HTTP_400_BAD_REQUEST,
                            data={"detail": str(ex)})