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
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, )
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