Example #1
0
    def test_get_studentview_submitted_timed_exam_with_past_due_date(self, due_date, hide_after_due):
        """
        Test for get_student_view timed exam with the due date.
        """

        # exam is created with due datetime which has already passed
        exam_id = self._create_exam_with_due_time(is_proctored=False, due_date=due_date)
        if hide_after_due:
            update_exam(exam_id, hide_after_due=hide_after_due)

        # now create the timed_exam attempt in the submitted state
        self._create_exam_attempt(exam_id, status='submitted')

        rendered_response = get_student_view(
            user_id=self.user_id,
            course_id=self.course_id,
            content_id=self.content_id_for_exam_with_due_date,
            context={
                'is_proctored': False,
                'display_name': self.exam_name,
                'default_time_limit_mins': 10,
                'due_date': due_date,
            }
        )
        if datetime.now(pytz.UTC) < due_date:
            self.assertIn(self.timed_exam_submitted, rendered_response)
            self.assertIn(self.submitted_timed_exam_msg_with_due_date, rendered_response)
        elif hide_after_due:
            self.assertIn(self.timed_exam_submitted, rendered_response)
            self.assertNotIn(self.submitted_timed_exam_msg_with_due_date, rendered_response)
        else:
            self.assertIsNone(rendered_response)
    def test_get_studentview_submitted_timed_exam_with_past_due_date(
            self, due_date, hide_after_due):
        """
        Test for get_student_view timed exam with the due date.
        """

        # exam is created with due datetime which has already passed
        exam_id = self._create_exam_with_due_time(is_proctored=False,
                                                  due_date=due_date)
        if hide_after_due:
            update_exam(exam_id, hide_after_due=hide_after_due)

        # now create the timed_exam attempt in the submitted state
        self._create_exam_attempt(exam_id, status='submitted')

        rendered_response = get_student_view(
            user_id=self.user_id,
            course_id=self.course_id,
            content_id=self.content_id_for_exam_with_due_date,
            context={
                'is_proctored': False,
                'display_name': self.exam_name,
                'default_time_limit_mins': 10,
                'due_date': due_date,
            })
        if datetime.now(pytz.UTC) < due_date:
            self.assertIn(self.timed_exam_submitted, rendered_response)
            self.assertIn(self.submitted_timed_exam_msg_with_due_date,
                          rendered_response)
        elif hide_after_due:
            self.assertIn(self.timed_exam_submitted, rendered_response)
            self.assertNotIn(self.submitted_timed_exam_msg_with_due_date,
                             rendered_response)
        else:
            self.assertIsNone(rendered_response)
Example #3
0
 def test_get_studentview_long_limit(self, under_exception):
     """
     Test for hide_extra_time_footer on exams with > 20 hours time limit
     """
     exam_id = self._create_exam_with_due_time(is_proctored=False, )
     if under_exception:
         update_exam(exam_id, time_limit_mins=((20 * 60)))  # exactly 20 hours
     else:
         update_exam(exam_id, time_limit_mins=((20 * 60) + 1))  # 1 minute greater than 20 hours
     rendered_response = get_student_view(
         user_id=self.user_id,
         course_id=self.course_id,
         content_id=self.content_id_for_exam_with_due_date,
         context={
             'is_proctored': False,
             'display_name': self.exam_name,
         }
     )
     if under_exception:
         self.assertIn(self.timed_footer_msg, rendered_response)
     else:
         self.assertNotIn(self.timed_footer_msg, rendered_response)
Example #4
0
 def put(self, request):
     """
     HTTP PUT handler. To update an exam.
     calls the update_exam
     """
     exam_id = update_exam(
         exam_id=request.data.get('exam_id', None),
         exam_name=request.data.get('exam_name', None),
         time_limit_mins=request.data.get('time_limit_mins', None),
         is_proctored=request.data.get('is_proctored', None),
         is_practice_exam=request.data.get('is_practice_exam', None),
         external_id=request.data.get('external_id', None),
         is_active=request.data.get('is_active', None),
         hide_after_due=request.data.get('hide_after_due', None),
     )
     return Response({'exam_id': exam_id})
Example #5
0
 def put(self, request):
     """
     HTTP PUT handler. To update an exam.
     calls the update_exam
     """
     exam_id = update_exam(
         exam_id=request.data.get('exam_id', None),
         exam_name=request.data.get('exam_name', None),
         time_limit_mins=request.data.get('time_limit_mins', None),
         is_proctored=request.data.get('is_proctored', None),
         is_practice_exam=request.data.get('is_practice_exam', None),
         external_id=request.data.get('external_id', None),
         is_active=request.data.get('is_active', None),
         hide_after_due=request.data.get('hide_after_due', None),
     )
     return Response({'exam_id': exam_id})
Example #6
0
 def put(self, request):
     """
     HTTP PUT handler. To update an exam.
     calls the update_exam
     """
     try:
         exam_id = update_exam(
             exam_id=request.data.get("exam_id", None),
             exam_name=request.data.get("exam_name", None),
             time_limit_mins=request.data.get("time_limit_mins", None),
             is_proctored=request.data.get("is_proctored", None),
             is_practice_exam=request.data.get("is_practice_exam", None),
             external_id=request.data.get("external_id", None),
             is_active=request.data.get("is_active", None),
         )
         return Response({"exam_id": exam_id})
     except ProctoredExamNotFoundException, ex:
         LOG.exception(ex)
         return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": "The exam_id does not exist."})
Example #7
0
 def put(self, request):
     """
     HTTP PUT handler. To update an exam.
     calls the update_exam
     """
     try:
         exam_id = update_exam(
             exam_id=request.data.get('exam_id', None),
             exam_name=request.data.get('exam_name', None),
             time_limit_mins=request.data.get('time_limit_mins', None),
             is_proctored=request.data.get('is_proctored', None),
             is_practice_exam=request.data.get('is_practice_exam', None),
             external_id=request.data.get('external_id', None),
             is_active=request.data.get('is_active', None),
         )
         return Response({'exam_id': exam_id})
     except ProctoredExamNotFoundException, ex:
         LOG.exception(ex)
         return Response(status=status.HTTP_400_BAD_REQUEST,
                         data={"detail": "The exam_id does not exist."})
Example #8
0
def register_special_exams(course_key):
    """
    This is typically called on a course published signal. The course is examined for sequences
    that are marked as timed exams. Then these are registered with the edx-proctoring
    subsystem. Likewise, if formerly registered exams are unmarked, then those
    registered exams are marked as inactive
    """

    if not settings.FEATURES.get('ENABLE_SPECIAL_EXAMS'):
        # if feature is not enabled then do a quick exit
        return

    course = modulestore().get_course(course_key)
    if not course.enable_proctored_exams and not course.enable_timed_exams:
        # likewise if course does not have these features turned on
        # then quickly exit
        return

    # get all sequences, since they can be marked as timed/proctored exams
    _timed_exams = modulestore().get_items(course_key,
                                           qualifiers={
                                               'category': 'sequential',
                                           },
                                           settings={
                                               'is_time_limited': True,
                                           })

    # filter out any potential dangling sequences
    timed_exams = [
        timed_exam for timed_exam in _timed_exams
        if is_item_in_course_tree(timed_exam)
    ]

    # enumerate over list of sequences which are time-limited and
    # add/update any exam entries in edx-proctoring
    for timed_exam in timed_exams:
        msg = (
            'Found {location} as a timed-exam in course structure. Inspecting...'
            .format(location=unicode(timed_exam.location)))
        log.info(msg)

        try:
            exam = get_exam_by_content_id(unicode(course_key),
                                          unicode(timed_exam.location))
            # update case, make sure everything is synced
            exam_id = update_exam(
                exam_id=exam['id'],
                exam_name=timed_exam.display_name,
                time_limit_mins=timed_exam.default_time_limit_minutes,
                due_date=timed_exam.due,
                is_proctored=timed_exam.is_proctored_exam,
                is_practice_exam=timed_exam.is_practice_exam,
                is_active=True,
                hide_after_due=timed_exam.hide_after_due,
            )
            msg = 'Updated timed exam {exam_id}'.format(exam_id=exam['id'])
            log.info(msg)

        except ProctoredExamNotFoundException:
            exam_id = create_exam(
                course_id=unicode(course_key),
                content_id=unicode(timed_exam.location),
                exam_name=timed_exam.display_name,
                time_limit_mins=timed_exam.default_time_limit_minutes,
                due_date=timed_exam.due,
                is_proctored=timed_exam.is_proctored_exam,
                is_practice_exam=timed_exam.is_practice_exam,
                is_active=True,
                hide_after_due=timed_exam.hide_after_due,
            )
            msg = 'Created new timed exam {exam_id}'.format(exam_id=exam_id)
            log.info(msg)

        # only create/update exam policy for the proctored exams
        if timed_exam.is_proctored_exam and not timed_exam.is_practice_exam:
            try:
                update_review_policy(
                    exam_id=exam_id,
                    set_by_user_id=timed_exam.edited_by,
                    review_policy=timed_exam.exam_review_rules)
            except ProctoredExamReviewPolicyNotFoundException:
                if timed_exam.exam_review_rules:  # won't save an empty rule.
                    create_exam_review_policy(
                        exam_id=exam_id,
                        set_by_user_id=timed_exam.edited_by,
                        review_policy=timed_exam.exam_review_rules)
                    msg = 'Created new exam review policy with exam_id {exam_id}'.format(
                        exam_id=exam_id)
                    log.info(msg)
        else:
            try:
                # remove any associated review policy
                remove_review_policy(exam_id=exam_id)
            except ProctoredExamReviewPolicyNotFoundException:
                pass

    # then see which exams we have in edx-proctoring that are not in
    # our current list. That means the the user has disabled it
    exams = get_all_exams_for_course(course_key)

    for exam in exams:
        if exam['is_active']:
            # try to look up the content_id in the sequences location

            search = [
                timed_exam for timed_exam in timed_exams
                if unicode(timed_exam.location) == exam['content_id']
            ]
            if not search:
                # This means it was turned off in Studio, we need to mark
                # the exam as inactive (we don't delete!)
                msg = 'Disabling timed exam {exam_id}'.format(
                    exam_id=exam['id'])
                log.info(msg)
                update_exam(
                    exam_id=exam['id'],
                    is_proctored=False,
                    is_active=False,
                )
Example #9
0
def register_special_exams(course_key):
    """
    This is typically called on a course published signal. The course is examined for sequences
    that are marked as timed exams. Then these are registered with the edx-proctoring
    subsystem. Likewise, if formerly registered exams are unmarked, then those
    registered exams are marked as inactive
    """
    if not settings.FEATURES.get('ENABLE_SPECIAL_EXAMS'):
        # if feature is not enabled then do a quick exit
        return

    course = modulestore().get_course(course_key)
    if course is None:
        raise ItemNotFoundError(u"Course {} does not exist", unicode(course_key))

    if not course.enable_proctored_exams and not course.enable_timed_exams:
        # likewise if course does not have these features turned on
        # then quickly exit
        return

    # get all sequences, since they can be marked as timed/proctored exams
    _timed_exams = modulestore().get_items(
        course_key,
        qualifiers={
            'category': 'sequential',
        },
        settings={
            'is_time_limited': True,
        }
    )

    # filter out any potential dangling sequences
    timed_exams = [
        timed_exam
        for timed_exam in _timed_exams
        if is_item_in_course_tree(timed_exam)
    ]

    # enumerate over list of sequences which are time-limited and
    # add/update any exam entries in edx-proctoring
    for timed_exam in timed_exams:
        msg = (
            u'Found {location} as a timed-exam in course structure. Inspecting...'.format(
                location=unicode(timed_exam.location)
            )
        )
        log.info(msg)

        exam_metadata = {
            'exam_name': timed_exam.display_name,
            'time_limit_mins': timed_exam.default_time_limit_minutes,
            'due_date': timed_exam.due,
            'is_proctored': timed_exam.is_proctored_exam,
            # backends that support onboarding exams will treat onboarding exams as practice
            'is_practice_exam': timed_exam.is_practice_exam or timed_exam.is_onboarding_exam,
            'is_active': True,
            'hide_after_due': timed_exam.hide_after_due,
            'backend': course.proctoring_provider,
        }

        try:
            exam = get_exam_by_content_id(unicode(course_key), unicode(timed_exam.location))
            # update case, make sure everything is synced
            exam_metadata['exam_id'] = exam['id']

            exam_id = update_exam(**exam_metadata)
            msg = u'Updated timed exam {exam_id}'.format(exam_id=exam['id'])
            log.info(msg)

        except ProctoredExamNotFoundException:
            exam_metadata['course_id'] = unicode(course_key)
            exam_metadata['content_id'] = unicode(timed_exam.location)

            exam_id = create_exam(**exam_metadata)
            msg = u'Created new timed exam {exam_id}'.format(exam_id=exam_id)
            log.info(msg)

        exam_review_policy_metadata = {
            'exam_id': exam_id,
            'set_by_user_id': timed_exam.edited_by,
            'review_policy': timed_exam.exam_review_rules,
        }

        # only create/update exam policy for the proctored exams
        if timed_exam.is_proctored_exam and not timed_exam.is_practice_exam and not timed_exam.is_onboarding_exam:
            try:
                update_review_policy(**exam_review_policy_metadata)
            except ProctoredExamReviewPolicyNotFoundException:
                if timed_exam.exam_review_rules:  # won't save an empty rule.
                    create_exam_review_policy(**exam_review_policy_metadata)
                    msg = u'Created new exam review policy with exam_id {exam_id}'.format(exam_id=exam_id)
                    log.info(msg)
        else:
            try:
                # remove any associated review policy
                remove_review_policy(exam_id=exam_id)
            except ProctoredExamReviewPolicyNotFoundException:
                pass

    # then see which exams we have in edx-proctoring that are not in
    # our current list. That means the the user has disabled it
    exams = get_all_exams_for_course(course_key)

    for exam in exams:
        if exam['is_active']:
            # try to look up the content_id in the sequences location

            search = [
                timed_exam for timed_exam in timed_exams if
                unicode(timed_exam.location) == exam['content_id']
            ]
            if not search:
                # This means it was turned off in Studio, we need to mark
                # the exam as inactive (we don't delete!)
                msg = u'Disabling timed exam {exam_id}'.format(exam_id=exam['id'])
                log.info(msg)
                update_exam(
                    exam_id=exam['id'],
                    is_proctored=False,
                    is_active=False,
                )
Example #10
0
def register_special_exams(course_key):
    """
    This is typically called on a course published signal. The course is examined for sequences
    that are marked as timed exams. Then these are registered with the edx-proctoring
    subsystem. Likewise, if formerly registered exams are unmarked, then those
    registered exams are marked as inactive
    """
    if not settings.FEATURES.get('ENABLE_SPECIAL_EXAMS'):
        # if feature is not enabled then do a quick exit
        return

    course = modulestore().get_course(course_key)
    if course is None:
        raise ItemNotFoundError(u"Course {} does not exist",
                                six.text_type(course_key))  # lint-amnesty, pylint: disable=raising-format-tuple

    if not course.enable_proctored_exams and not course.enable_timed_exams:
        # likewise if course does not have these features turned on
        # then quickly exit
        return

    # get all sequences, since they can be marked as timed/proctored exams
    _timed_exams = modulestore().get_items(course_key,
                                           qualifiers={
                                               'category': 'sequential',
                                           },
                                           settings={
                                               'is_time_limited': True,
                                           })

    # filter out any potential dangling sequences
    timed_exams = [
        timed_exam for timed_exam in _timed_exams
        if is_item_in_course_tree(timed_exam)
    ]

    # enumerate over list of sequences which are time-limited and
    # add/update any exam entries in edx-proctoring
    for timed_exam in timed_exams:
        msg = (
            u'Found {location} as a timed-exam in course structure. Inspecting...'
            .format(location=six.text_type(timed_exam.location)))
        log.info(msg)

        exam_metadata = {
            'exam_name': timed_exam.display_name,
            'time_limit_mins': timed_exam.default_time_limit_minutes,
            'due_date': timed_exam.due if not course.self_paced else None,
            'is_proctored': timed_exam.is_proctored_exam,
            # backends that support onboarding exams will treat onboarding exams as practice
            'is_practice_exam': timed_exam.is_practice_exam
            or timed_exam.is_onboarding_exam,
            'is_active': True,
            'hide_after_due': timed_exam.hide_after_due,
            'backend': course.proctoring_provider,
        }

        try:
            exam = get_exam_by_content_id(six.text_type(course_key),
                                          six.text_type(timed_exam.location))
            # update case, make sure everything is synced
            exam_metadata['exam_id'] = exam['id']

            exam_id = update_exam(**exam_metadata)
            msg = u'Updated timed exam {exam_id}'.format(exam_id=exam['id'])
            log.info(msg)

        except ProctoredExamNotFoundException:
            exam_metadata['course_id'] = six.text_type(course_key)
            exam_metadata['content_id'] = six.text_type(timed_exam.location)

            exam_id = create_exam(**exam_metadata)
            msg = u'Created new timed exam {exam_id}'.format(exam_id=exam_id)
            log.info(msg)

        exam_review_policy_metadata = {
            'exam_id': exam_id,
            'set_by_user_id': timed_exam.edited_by,
            'review_policy': timed_exam.exam_review_rules,
        }

        # only create/update exam policy for the proctored exams
        if timed_exam.is_proctored_exam and not timed_exam.is_practice_exam and not timed_exam.is_onboarding_exam:
            try:
                update_review_policy(**exam_review_policy_metadata)
            except ProctoredExamReviewPolicyNotFoundException:
                if timed_exam.exam_review_rules:  # won't save an empty rule.
                    create_exam_review_policy(**exam_review_policy_metadata)
                    msg = u'Created new exam review policy with exam_id {exam_id}'.format(
                        exam_id=exam_id)
                    log.info(msg)
        else:
            try:
                # remove any associated review policy
                remove_review_policy(exam_id=exam_id)
            except ProctoredExamReviewPolicyNotFoundException:
                pass

    # then see which exams we have in edx-proctoring that are not in
    # our current list. That means the the user has disabled it
    exams = get_all_exams_for_course(course_key)

    for exam in exams:
        if exam['is_active']:
            # try to look up the content_id in the sequences location

            search = [
                timed_exam for timed_exam in timed_exams
                if six.text_type(timed_exam.location) == exam['content_id']
            ]
            if not search:
                # This means it was turned off in Studio, we need to mark
                # the exam as inactive (we don't delete!)
                msg = u'Disabling timed exam {exam_id}'.format(
                    exam_id=exam['id'])
                log.info(msg)
                update_exam(
                    exam_id=exam['id'],
                    is_proctored=False,
                    is_active=False,
                )
Example #11
0
def register_special_exams(course_key):
    """
    This is typically called on a course published signal. The course is examined for sequences
    that are marked as timed exams. Then these are registered with the edx-proctoring
    subsystem. Likewise, if formerly registered exams are unmarked, then those
    registered exams are marked as inactive
    """

    if not settings.FEATURES.get("ENABLE_SPECIAL_EXAMS"):
        # if feature is not enabled then do a quick exit
        return

    course = modulestore().get_course(course_key)
    if not course.enable_proctored_exams and not course.enable_timed_exams:
        # likewise if course does not have these features turned on
        # then quickly exit
        return

    # get all sequences, since they can be marked as timed/proctored exams
    _timed_exams = modulestore().get_items(
        course_key, qualifiers={"category": "sequential"}, settings={"is_time_limited": True}
    )

    # filter out any potential dangling sequences
    timed_exams = [timed_exam for timed_exam in _timed_exams if is_item_in_course_tree(timed_exam)]

    # enumerate over list of sequences which are time-limited and
    # add/update any exam entries in edx-proctoring
    for timed_exam in timed_exams:
        msg = "Found {location} as a timed-exam in course structure. Inspecting...".format(
            location=unicode(timed_exam.location)
        )
        log.info(msg)

        try:
            exam = get_exam_by_content_id(unicode(course_key), unicode(timed_exam.location))
            # update case, make sure everything is synced
            update_exam(
                exam_id=exam["id"],
                exam_name=timed_exam.display_name,
                time_limit_mins=timed_exam.default_time_limit_minutes,
                due_date=timed_exam.due,
                is_proctored=timed_exam.is_proctored_exam,
                is_practice_exam=timed_exam.is_practice_exam,
                is_active=True,
            )
            msg = "Updated timed exam {exam_id}".format(exam_id=exam["id"])
            log.info(msg)
        except ProctoredExamNotFoundException:
            exam_id = create_exam(
                course_id=unicode(course_key),
                content_id=unicode(timed_exam.location),
                exam_name=timed_exam.display_name,
                time_limit_mins=timed_exam.default_time_limit_minutes,
                due_date=timed_exam.due,
                is_proctored=timed_exam.is_proctored_exam,
                is_practice_exam=timed_exam.is_practice_exam,
                is_active=True,
            )
            msg = "Created new timed exam {exam_id}".format(exam_id=exam_id)
            log.info(msg)

    # then see which exams we have in edx-proctoring that are not in
    # our current list. That means the the user has disabled it
    exams = get_all_exams_for_course(course_key)

    for exam in exams:
        if exam["is_active"]:
            # try to look up the content_id in the sequences location

            search = [timed_exam for timed_exam in timed_exams if unicode(timed_exam.location) == exam["content_id"]]
            if not search:
                # This means it was turned off in Studio, we need to mark
                # the exam as inactive (we don't delete!)
                msg = "Disabling timed exam {exam_id}".format(exam_id=exam["id"])
                log.info(msg)
                update_exam(exam_id=exam["id"], is_proctored=False, is_active=False)