Beispiel #1
0
def _add_timed_exam_info(user, course, section, section_context):
    """
    Add in rendering context if exam is a timed exam (which includes proctored)
    """
    section_is_time_limited = (
        getattr(section, 'is_time_limited', False) and
        settings.FEATURES.get('ENABLE_SPECIAL_EXAMS', False)
    )
    if section_is_time_limited:
        # We need to import this here otherwise Lettuce test
        # harness fails. When running in 'harvest' mode, the
        # test service appears to get into trouble with
        # circular references (not sure which as edx_proctoring.api
        # doesn't import anything from edx-platform). Odd thing
        # is that running: manage.py lms runserver --settings=acceptance
        # works just fine, it's really a combination of Lettuce and the
        # 'harvest' management command
        #
        # One idea is that there is some coupling between
        # lettuce and the 'terrain' Djangoapps projects in /common
        # This would need more investigation
        from edx_proctoring.api import get_attempt_status_summary

        #
        # call into edx_proctoring subsystem
        # to get relevant proctoring information regarding this
        # level of the courseware
        #
        # This will return None, if (user, course_id, content_id)
        # is not applicable
        #
        timed_exam_attempt_context = None
        try:
            timed_exam_attempt_context = get_attempt_status_summary(
                user.id,
                unicode(course.id),
                unicode(section.location)
            )
        except Exception, ex:  # pylint: disable=broad-except
            # safety net in case something blows up in edx_proctoring
            # as this is just informational descriptions, it is better
            # to log and continue (which is safe) than to have it be an
            # unhandled exception
            log.exception(ex)

        if timed_exam_attempt_context:
            # yes, user has proctoring context about
            # this level of the courseware
            # so add to the accordion data context
            section_context.update({
                'proctoring': timed_exam_attempt_context,
            })
def _add_timed_exam_info(user, course, section, section_context):
    """
    Add in rendering context if exam is a timed exam (which includes proctored)
    """
    section_is_time_limited = (
        getattr(section, 'is_time_limited', False) and
        settings.FEATURES.get('ENABLE_SPECIAL_EXAMS', False)
    )
    if section_is_time_limited:
        # We need to import this here otherwise Lettuce test
        # harness fails. When running in 'harvest' mode, the
        # test service appears to get into trouble with
        # circular references (not sure which as edx_proctoring.api
        # doesn't import anything from edx-platform). Odd thing
        # is that running: manage.py lms runserver --settings=acceptance
        # works just fine, it's really a combination of Lettuce and the
        # 'harvest' management command
        #
        # One idea is that there is some coupling between
        # lettuce and the 'terrain' Djangoapps projects in /common
        # This would need more investigation
        from edx_proctoring.api import get_attempt_status_summary

        #
        # call into edx_proctoring subsystem
        # to get relevant proctoring information regarding this
        # level of the courseware
        #
        # This will return None, if (user, course_id, content_id)
        # is not applicable
        #
        timed_exam_attempt_context = None
        try:
            timed_exam_attempt_context = get_attempt_status_summary(
                user.id,
                unicode(course.id),
                unicode(section.location)
            )
        except Exception, ex:  # pylint: disable=broad-except
            # safety net in case something blows up in edx_proctoring
            # as this is just informational descriptions, it is better
            # to log and continue (which is safe) than to have it be an
            # unhandled exception
            log.exception(ex)

        if timed_exam_attempt_context:
            # yes, user has proctoring context about
            # this level of the courseware
            # so add to the accordion data context
            section_context.update({
                'proctoring': timed_exam_attempt_context,
            })
 def is_proctored_exam_for_user(block_key):
     """
     Test whether the block is a proctored exam for the user in
     question.
     """
     if (
             block_key.block_type == 'sequential' and (
                 block_structure.get_xblock_field(block_key, 'is_proctored_enabled') or
                 block_structure.get_xblock_field(block_key, 'is_practice_exam')
             )
     ):
         # This section is an exam.  It should be excluded unless the
         # user is not a verified student or has declined taking the exam.
         user_exam_summary = get_attempt_status_summary(
             usage_info.user.id,
             unicode(block_key.course_key),
             unicode(block_key),
         )
         return user_exam_summary and user_exam_summary['status'] != ProctoredExamStudentAttemptStatus.declined
Beispiel #4
0
    def exam_data(self, pruned_course_outline: UserCourseOutlineData) -> SpecialExamAttemptData:
        """
        Return supplementary special exam information for this outline.

        Be careful to pass in a UserCourseOutlineData - i.e. an outline that has
        already been pruned to what a user is allowed to see. That way, we can
        use this to make sure that we're not returning data about
        LearningSequences that the user can't see because it was hidden by a
        different OutlineProcessor.
        """
        sequences = {}
        if self.special_exams_enabled:
            for section in pruned_course_outline.sections:
                for sequence in section.sequences:
                    # Don't bother checking for information
                    # on non-exam sequences
                    if not bool(sequence.exam):
                        continue

                    special_exam_attempt_context = None
                    try:
                        # Calls into edx_proctoring subsystem to get relevant special exam information.
                        # This will return None, if (user, course_id, content_id) is not applicable.
                        special_exam_attempt_context = get_attempt_status_summary(
                            self.user.id,
                            str(self.course_key),
                            str(sequence.usage_key)
                        )
                    except ProctoredExamNotFoundException:
                        log.info(
                            'No exam found for {sequence_key} in {course_key}'.format(
                                sequence_key=sequence.usage_key,
                                course_key=self.course_key
                            )
                        )

                    if special_exam_attempt_context:
                        # Return exactly the same format as the edx_proctoring API response
                        sequences[sequence.usage_key] = special_exam_attempt_context

        return SpecialExamAttemptData(
            sequences=sequences,
        )
 def is_proctored_exam_for_user(block_key):
     """
     Test whether the block is a proctored exam for the user in
     question.
     """
     if (block_key.block_type == 'sequential'
             and (block_structure.get_xblock_field(
                 block_key, 'is_proctored_enabled')
                  or block_structure.get_xblock_field(
                      block_key, 'is_practice_exam'))):
         # This section is an exam.  It should be excluded unless the
         # user is not a verified student or has declined taking the exam.
         user_exam_summary = get_attempt_status_summary(
             usage_info.user.id,
             unicode(block_key.course_key),
             unicode(block_key),
         )
         return user_exam_summary and user_exam_summary[
             'status'] != ProctoredExamStudentAttemptStatus.declined
    def add_special_exam_info(self, block_key, block_structure, usage_info):
        """
        For special exams, add the special exam information to the course blocks.
        """
        special_exam_attempt_context = None
        try:
            # Calls into edx_proctoring subsystem to get relevant special exam information.
            # This will return None, if (user, course_id, content_id) is not applicable.
            special_exam_attempt_context = get_attempt_status_summary(
                usage_info.user.id, six.text_type(block_key.course_key),
                six.text_type(block_key))
        except ProctoredExamNotFoundException as ex:
            log.exception(ex)

        if special_exam_attempt_context:
            # This user has special exam context for this block so add it.
            block_structure.set_transformer_block_field(
                block_key,
                self,
                'special_exam_info',
                special_exam_attempt_context,
            )
    def add_special_exam_info(self, block_key, block_structure, usage_info):
        """
        For special exams, add the special exam information to the course blocks.
        """
        special_exam_attempt_context = None
        try:
            # Calls into edx_proctoring subsystem to get relevant special exam information.
            # This will return None, if (user, course_id, content_id) is not applicable.
            special_exam_attempt_context = get_attempt_status_summary(
                usage_info.user.id,
                unicode(block_key.course_key),
                unicode(block_key)
            )
        except ProctoredExamNotFoundException as ex:
            log.exception(ex)

        if special_exam_attempt_context:
            # This user has special exam context for this block so add it.
            block_structure.set_transformer_block_field(
                block_key,
                self,
                'special_exam_info',
                special_exam_attempt_context,
            )
Beispiel #8
0
def toc_for_course(user, request, course, active_chapter, active_section, field_data_cache):
    '''
    Create a table of contents from the module store

    Return format:
    [ {'display_name': name, 'url_name': url_name,
       'sections': SECTIONS, 'active': bool}, ... ]

    where SECTIONS is a list
    [ {'display_name': name, 'url_name': url_name,
       'format': format, 'due': due, 'active' : bool, 'graded': bool}, ...]

    active is set for the section and chapter corresponding to the passed
    parameters, which are expected to be url_names of the chapter+section.
    Everything else comes from the xml, or defaults to "".

    chapters with name 'hidden' are skipped.

    NOTE: assumes that if we got this far, user has access to course.  Returns
    None if this is not the case.

    field_data_cache must include data from the course module and 2 levels of its descendents
    '''

    with modulestore().bulk_operations(course.id):
        course_module = get_module_for_descriptor(
            user, request, course, field_data_cache, course.id, course=course
        )
        if course_module is None:
            return None

        toc_chapters = list()
        chapters = course_module.get_display_items()

        # See if the course is gated by one or more content milestones
        required_content = milestones_helpers.get_required_content(course, user)

        # The user may not actually have to complete the entrance exam, if one is required
        if not user_must_complete_entrance_exam(request, user, course):
            required_content = [content for content in required_content if not content == course.entrance_exam_id]

        for chapter in chapters:
            # Only show required content, if there is required content
            # chapter.hide_from_toc is read-only (boo)
            display_id = slugify(chapter.display_name_with_default)
            local_hide_from_toc = False
            if required_content:
                if unicode(chapter.location) not in required_content:
                    local_hide_from_toc = True

            # Skip the current chapter if a hide flag is tripped
            if chapter.hide_from_toc or local_hide_from_toc:
                continue

            sections = list()
            for section in chapter.get_display_items():

                active = (chapter.url_name == active_chapter and
                          section.url_name == active_section)

                if not section.hide_from_toc:
                    section_context = {
                        'display_name': section.display_name_with_default,
                        'url_name': section.url_name,
                        'format': section.format if section.format is not None else '',
                        'due': section.due,
                        'active': active,
                        'graded': section.graded,
                    }

                    #
                    # Add in rendering context if exam is a timed exam (which includes proctored)
                    #

                    section_is_time_limited = (
                        getattr(section, 'is_time_limited', False) and
                        settings.FEATURES.get('ENABLE_SPECIAL_EXAMS', False)
                    )
                    if section_is_time_limited:
                        # We need to import this here otherwise Lettuce test
                        # harness fails. When running in 'harvest' mode, the
                        # test service appears to get into trouble with
                        # circular references (not sure which as edx_proctoring.api
                        # doesn't import anything from edx-platform). Odd thing
                        # is that running: manage.py lms runserver --settings=acceptance
                        # works just fine, it's really a combination of Lettuce and the
                        # 'harvest' management command
                        #
                        # One idea is that there is some coupling between
                        # lettuce and the 'terrain' Djangoapps projects in /common
                        # This would need more investigation
                        from edx_proctoring.api import get_attempt_status_summary

                        #
                        # call into edx_proctoring subsystem
                        # to get relevant proctoring information regarding this
                        # level of the courseware
                        #
                        # This will return None, if (user, course_id, content_id)
                        # is not applicable
                        #
                        timed_exam_attempt_context = None
                        try:
                            timed_exam_attempt_context = get_attempt_status_summary(
                                user.id,
                                unicode(course.id),
                                unicode(section.location)
                            )
                        except Exception, ex:  # pylint: disable=broad-except
                            # safety net in case something blows up in edx_proctoring
                            # as this is just informational descriptions, it is better
                            # to log and continue (which is safe) than to have it be an
                            # unhandled exception
                            log.exception(ex)

                        if timed_exam_attempt_context:
                            # yes, user has proctoring context about
                            # this level of the courseware
                            # so add to the accordion data context
                            section_context.update({
                                'proctoring': timed_exam_attempt_context,
                            })

                    sections.append(section_context)
            toc_chapters.append({
                'display_name': chapter.display_name_with_default,
                'display_id': display_id,
                'url_name': chapter.url_name,
                'sections': sections,
                'active': chapter.url_name == active_chapter
            })
        return toc_chapters