def test_hidden_content_with_transformer_override(self): """ Tests content is hidden if the date changes after collection and during the transform phase (for example, by the DateOverrideTransformer). """ with mock_registered_transformers( [DateOverrideTransformer, self.TRANSFORMER_CLASS_TO_TEST]): transformers = BlockStructureTransformers([ DateOverrideTransformer(self.student), self.TRANSFORMER_CLASS_TO_TEST() ]) block = self.get_block(1) block.hide_after_due = True update_block(block) set_date_for_block(self.course.id, block.location, 'due', self.DueDateType.PAST_DATE) # Due date is in the past so some blocks are hidden self.assert_transform_results( self.student, self.ALL_BLOCKS - {1, 3, 4}, blocks_with_differing_access=None, transformers=transformers, ) # Set an override for the due date to be in the future set_date_for_block(self.course.id, block.location, 'due', self.DueDateType.FUTURE_DATE, user=self.student) # this line is just to bust the cache for the user so it returns the updated date. get_dates_for_course(self.course.id, user=self.student, use_cached=False) # Now all blocks are returned for the student self.assert_transform_results( self.student, self.ALL_BLOCKS, blocks_with_differing_access=None, transformers=transformers, ) # But not for a different user different_user = UserFactory() with mock_registered_transformers( [DateOverrideTransformer, self.TRANSFORMER_CLASS_TO_TEST]): transformers = BlockStructureTransformers([ DateOverrideTransformer(different_user), self.TRANSFORMER_CLASS_TO_TEST() ]) self.assert_transform_results( different_user, self.ALL_BLOCKS - {1, 3, 4}, blocks_with_differing_access=None, transformers=transformers, )
def get_course_blocks( user, starting_block_usage_key, transformers=None, collected_block_structure=None, allow_start_dates_in_future=False, include_completion=False, include_has_scheduled_content=False, ): """ A higher order function implemented on top of the block_structure.get_blocks function returning a transformed block structure for the given user starting at starting_block_usage_key. Arguments: user (django.contrib.auth.models.User) - User object for which the block structure is to be transformed. starting_block_usage_key (UsageKey) - Specifies the starting block of the block structure that is to be transformed. transformers (BlockStructureTransformers) - A collection of transformers whose transform methods are to be called. If None, get_course_block_access_transformers() is used. collected_block_structure (BlockStructureBlockData) - A block structure retrieved from a prior call to BlockStructureManager.get_collected. Can be optionally provided if already available, for optimization. Returns: BlockStructureBlockData - A transformed block structure, starting at starting_block_usage_key, that has undergone the transform methods for the given user and the course associated with the block structure. If using the default transformers, the transformed block structure will be exactly equivalent to the blocks that the given user has access. """ if not transformers: transformers = BlockStructureTransformers( get_course_block_access_transformers(user)) if include_completion: transformers += [BlockCompletionTransformer()] transformers.usage_info = CourseUsageInfo( starting_block_usage_key.course_key, user, allow_start_dates_in_future, include_has_scheduled_content) return get_block_structure_manager( starting_block_usage_key.course_key).get_transformed( transformers, starting_block_usage_key, collected_block_structure, )
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) course_usage_key = modulestore().make_course_usage_key(course_key) # Enable NR tracing for this view based on course monitoring_utils.set_custom_metric('course_id', course_key_string) monitoring_utils.set_custom_metric('user_id', request.user.id) monitoring_utils.set_custom_metric('is_staff', request.user.is_staff) course_tools = CourseToolsPluginManager.get_enabled_course_tools(request, course_key) transformers = BlockStructureTransformers() transformers += course_blocks_api.get_course_block_access_transformers(request.user) transformers += [ BlocksAPITransformer(None, None, depth=3), ] course_blocks = get_course_blocks(request.user, course_usage_key, transformers, include_completion=True) data = { 'course_tools': course_tools, 'course_blocks': course_blocks, } context = self.get_serializer_context() context['course_key'] = course_key serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def test_special_exams(self): """ When the block structure transformers are set to allow users to view special exams, ensure that we can see the special exams and not any of the otherwise gated blocks. """ self.transformers = BlockStructureTransformers( [self.TRANSFORMER_CLASS_TO_TEST(True)]) self.course.enable_subsection_gating = True self.setup_gated_section(self.blocks['H'], self.blocks['A']) expected_blocks = ('course', 'A', 'B', 'C', 'ProctoredExam', 'D', 'E', 'PracticeExam', 'F', 'G', 'TimedExam', 'J', 'K', 'H', 'I') self.get_blocks_and_check_against_expected(self.user, expected_blocks) # clear the request cache to simulate a new request self.clear_caches() # this call triggers reevaluation of prerequisites fulfilled by the gating block. with patch('openedx.core.lib.gating.api._get_subsection_percentage', Mock(return_value=100)): lms_gating_api.evaluate_prerequisite( self.course, Mock(location=self.blocks['A'].location), self.user, ) self.get_blocks_and_check_against_expected(self.user, self.ALL_BLOCKS)
def _map_block_usage_keys_to_display_names(cls, course_id): """ Fetches all course blocks and build mapping between block usage key string and block display name for those ones, whoose category is equal to ``openassessment``. Args: course_id (string or CourseLocator instance) - id of course resourse Returns: dictionary, that contains mapping between block usage keys (locations) and block display names. """ # pylint: disable=import-error from lms.djangoapps.course_blocks.api import get_course_blocks from openedx.core.djangoapps.content.block_structure.transformers import BlockStructureTransformers from xmodule.modulestore.django import modulestore store = modulestore() course_usage_key = store.make_course_usage_key(course_id) # Passing an empty block structure transformer here to avoid user access checks blocks = get_course_blocks(None, course_usage_key, BlockStructureTransformers()) block_display_name_map = {} for block_key in blocks: block_type = blocks.get_xblock_field(block_key, 'category') if block_type == 'openassessment': block_display_name_map[str(block_key)] = blocks.get_xblock_field(block_key, 'display_name') return block_display_name_map
def test_content_library(self): """ Test when course has content library section. First test user can't see any content library section, and after that mock response from MySQL db. Check user can see mocked sections in content library. """ raw_block_structure = get_course_blocks( self.user, self.course.location, transformers=BlockStructureTransformers(), ) self.assertEqual(len(list(raw_block_structure.get_block_keys())), len(self.blocks)) clear_course_from_cache(self.course.id) trans_block_structure = get_course_blocks( self.user, self.course.location, self.transformers, ) # Should dynamically assign a block to student trans_keys = set(trans_block_structure.get_block_keys()) block_key_set = self.get_block_key_set( self.blocks, 'course', 'chapter1', 'lesson1', 'vertical1', 'library_content1' ) for key in block_key_set: self.assertIn(key, trans_keys) vertical2_selected = self.get_block_key_set(self.blocks, 'vertical2').pop() in trans_keys vertical3_selected = self.get_block_key_set(self.blocks, 'vertical3').pop() in trans_keys self.assertNotEqual(vertical2_selected, vertical3_selected) # only one of them should be selected selected_vertical = 'vertical2' if vertical2_selected else 'vertical3' selected_child = 'html1' if vertical2_selected else 'html2' # Check course structure again. clear_course_from_cache(self.course.id) for i in range(5): trans_block_structure = get_course_blocks( self.user, self.course.location, self.transformers, ) self.assertEqual( set(trans_block_structure.get_block_keys()), self.get_block_key_set( self.blocks, 'course', 'chapter1', 'lesson1', 'vertical1', 'library_content1', selected_vertical, selected_child, ), u"Expected 'selected' equality failed in iteration {}.".format(i) )
def get_course_blocks( user, starting_block_usage_key, transformers=None, collected_block_structure=None, ): """ A higher order function implemented on top of the block_structure.get_blocks function returning a transformed block structure for the given user starting at starting_block_usage_key. Arguments: user (django.contrib.auth.models.User) - User object for which the block structure is to be transformed. starting_block_usage_key (UsageKey) - Specifies the starting block of the block structure that is to be transformed. transformers (BlockStructureTransformers) - A collection of transformers whose transform methods are to be called. If None, COURSE_BLOCK_ACCESS_TRANSFORMERS is used. collected_block_structure (BlockStructureBlockData) - A block structure retrieved from a prior call to BlockStructureManager.get_collected. Can be optionally provided if already available, for optimization. Returns: BlockStructureBlockData - A transformed block structure, starting at starting_block_usage_key, that has undergone the transform methods for the given user and the course associated with the block structure. If using the default transformers, the transformed block structure will be exactly equivalent to the blocks that the given user has access. """ if not transformers: transformers = BlockStructureTransformers(COURSE_BLOCK_ACCESS_TRANSFORMERS) transformers.usage_info = CourseUsageInfo(starting_block_usage_key.course_key, user) return get_block_structure_manager(starting_block_usage_key.course_key).get_transformed( transformers, starting_block_usage_key, collected_block_structure, )
def setUp(self): super(TransformerRegistryTestMixin, self).setUp() self.patcher = patch( 'openedx.core.djangoapps.content.block_structure.transformer_registry.' 'TransformerRegistry.get_registered_transformers') mock_registry = self.patcher.start() mock_registry.return_value = {self.TRANSFORMER_CLASS_TO_TEST} self.transformers = BlockStructureTransformers( [self.TRANSFORMER_CLASS_TO_TEST()])
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) course_usage_key = modulestore().make_course_usage_key(course_key) # Enable NR tracing for this view based on course monitoring_utils.set_custom_metric('course_id', course_key_string) monitoring_utils.set_custom_metric('user_id', request.user.id) monitoring_utils.set_custom_metric('is_staff', request.user.is_staff) _, request.user = setup_masquerade(request, course_key, staff_access=has_access( request.user, 'staff', course_key), reset_masquerade_data=True) user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] transformers = BlockStructureTransformers() transformers += course_blocks_api.get_course_block_access_transformers( request.user) transformers += [ BlocksAPITransformer(None, None, depth=3), ] course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) course_blocks = get_course_blocks(request.user, course_usage_key, transformers, include_completion=True) enrollment_mode, _ = CourseEnrollment.enrollment_mode_for_user( request.user, course_key) course_grade = CourseGradeFactory().read(request.user, course) courseware_summary = course_grade.chapter_grades.values() data = { 'course_blocks': course_blocks, 'courseware_summary': courseware_summary, 'enrollment_mode': enrollment_mode, 'user_timezone': user_timezone, } context = self.get_serializer_context() context['staff_access'] = bool( has_access(request.user, 'staff', course)) context['course_key'] = course_key serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def setUp(self): """ Setup course structure and create user for split test transformer test. """ super(MilestonesTransformerTestCase, self).setUp() # Build course. self.course_hierarchy = self.get_course_hierarchy() self.blocks = self.build_course(self.course_hierarchy) self.course = self.blocks['course'] # Enroll user in course. CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id, is_active=True) self.transformers = BlockStructureTransformers([self.TRANSFORMER_CLASS_TO_TEST(False)])
def init_course_blocks(user, root_block_key): """ Return a BlockStructure representing the course. Blocks must have the following attributes: .location .block_type """ # pragma: no-cover from lms.djangoapps.course_blocks.api import get_course_block_access_transformers, get_course_blocks # pylint: disable=import-error from openedx.core.djangoapps.content.block_structure.transformers import BlockStructureTransformers # pylint: disable=import-error transformers = BlockStructureTransformers( get_course_block_access_transformers(user) + [AggregatorAnnotationTransformer()]) return get_course_blocks(user, root_block_key, transformers)
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) course_usage_key = modulestore().make_course_usage_key(course_key) # Enable NR tracing for this view based on course monitoring_utils.set_custom_metric('course_id', course_key_string) monitoring_utils.set_custom_metric('user_id', request.user.id) monitoring_utils.set_custom_metric('is_staff', request.user.is_staff) _, request.user = setup_masquerade(request, course_key, staff_access=has_access( request.user, 'staff', course_key), reset_masquerade_data=True) transformers = BlockStructureTransformers() transformers += course_blocks_api.get_course_block_access_transformers( request.user) transformers += [ BlocksAPITransformer(None, None, depth=3), ] get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) course_blocks = get_course_blocks(request.user, course_usage_key, transformers, include_completion=True) enrollment_mode, _ = CourseEnrollment.enrollment_mode_for_user( request.user, course_key) data = { 'course_blocks': course_blocks, 'enrollment_mode': enrollment_mode, } serializer = self.get_serializer(data) return Response(serializer.data)
def setUp(self): super(TestBlockSerializerBase, self).setUp() self.user = UserFactory.create() blocks_api_transformer = BlocksAPITransformer( block_types_to_count=['video'], requested_student_view_data=['video'], ) self.transformers = BlockStructureTransformers( COURSE_BLOCK_ACCESS_TRANSFORMERS + [blocks_api_transformer]) self.block_structure = get_course_blocks( self.user, self.course.location, self.transformers, ) self.serializer_context = { 'request': MagicMock(), 'block_structure': self.block_structure, 'requested_fields': ['type'], }
def setUp(self): super(TestBlockSerializerBase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments self.user = UserFactory.create() blocks_api_transformer = BlocksAPITransformer( block_types_to_count=['video'], requested_student_view_data=['video'], ) self.transformers = BlockStructureTransformers( get_course_block_access_transformers(self.user) + [blocks_api_transformer] ) self.block_structure = get_course_blocks( self.user, self.course.location, self.transformers, ) self.serializer_context = { 'request': MagicMock(), 'block_structure': self.block_structure, 'requested_fields': ['type'], }
def get_blocks( request, usage_key, user=None, depth=None, nav_depth=None, requested_fields=None, block_counts=None, student_view_data=None, return_type='dict', block_types_filter=None, hide_access_denials=False, allow_start_dates_in_future=False, ): """ Return a serialized representation of the course blocks. Arguments: request (HTTPRequest): Used for calling django reverse. usage_key (UsageKey): Identifies the starting block of interest. user (User): Optional user object for whom the blocks are being retrieved. If None, blocks are returned regardless of access checks. depth (integer or None): Identifies the depth of the tree to return starting at the root block. If None, the entire tree starting at the root is returned. nav_depth (integer): Optional parameter that indicates how far deep to traverse into the block hierarchy before bundling all the descendants for navigation. requested_fields (list): Optional list of names of additional fields to return for each block. Supported fields are listed in transformers.SUPPORTED_FIELDS. block_counts (list): Optional list of names of block types for which to return an aggregate count of blocks. student_view_data (list): Optional list of names of block types for which blocks to return their student_view_data. return_type (string): Possible values are 'dict' or 'list'. Indicates the format for returning the blocks. block_types_filter (list): Optional list of block type names used to filter the final result of returned blocks. hide_access_denials (bool): When True, filter out any blocks that were denied access to the user, even if they have access denial messages attached. allow_start_dates_in_future (bool): When True, will allow blocks to be returned that can bypass the StartDateTransformer's filter to show blocks with start dates in the future. """ if HIDE_ACCESS_DENIALS_FLAG.is_enabled(): hide_access_denials = True # create ordered list of transformers, adding BlocksAPITransformer at end. transformers = BlockStructureTransformers() if requested_fields is None: requested_fields = [] include_completion = 'completion' in requested_fields include_effort_estimation = (EffortEstimationTransformer.EFFORT_TIME in requested_fields or EffortEstimationTransformer.EFFORT_ACTIVITIES in requested_fields) include_gated_sections = 'show_gated_sections' in requested_fields include_has_scheduled_content = 'has_scheduled_content' in requested_fields include_special_exams = 'special_exam_info' in requested_fields if user is not None: transformers += course_blocks_api.get_course_block_access_transformers(user) transformers += [ MilestonesAndSpecialExamsTransformer( include_special_exams=include_special_exams, include_gated_sections=include_gated_sections ), HiddenContentTransformer() ] if hide_access_denials: transformers += [AccessDeniedMessageFilterTransformer()] # TODO: Remove this after REVE-52 lands and old-mobile-app traffic falls to < 5% of mobile traffic if is_request_from_mobile_app(request): transformers += [HideEmptyTransformer()] if include_effort_estimation: transformers += [EffortEstimationTransformer()] transformers += [ BlocksAPITransformer( block_counts, student_view_data, depth, nav_depth ) ] # transform blocks = course_blocks_api.get_course_blocks( user, usage_key, transformers, allow_start_dates_in_future=allow_start_dates_in_future, include_completion=include_completion, include_has_scheduled_content=include_has_scheduled_content ) # filter blocks by types if block_types_filter: block_keys_to_remove = [] for block_key in blocks: block_type = blocks.get_xblock_field(block_key, 'category') if block_type not in block_types_filter: block_keys_to_remove.append(block_key) for block_key in block_keys_to_remove: blocks.remove_block(block_key, keep_descendants=True) # serialize serializer_context = { 'request': request, 'block_structure': blocks, 'requested_fields': requested_fields or [], } if return_type == 'dict': serializer = BlockDictSerializer(blocks, context=serializer_context, many=False) else: serializer = BlockSerializer(blocks, context=serializer_context, many=True) # return serialized data return serializer.data
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) course_usage_key = modulestore().make_course_usage_key(course_key) if not course_home_mfe_outline_tab_is_active(course_key): raise Http404 # Enable NR tracing for this view based on course monitoring_utils.set_custom_metric('course_id', course_key_string) monitoring_utils.set_custom_metric('user_id', request.user.id) monitoring_utils.set_custom_metric('is_staff', request.user.is_staff) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) _masquerade, request.user = setup_masquerade( request, course_key, staff_access=has_access(request.user, 'staff', course_key), reset_masquerade_data=True, ) course_overview = CourseOverview.get_from_id(course_key) enrollment = CourseEnrollment.get_enrollment(request.user, course_key) allow_anonymous = COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled( course_key) allow_public = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC is_enrolled = enrollment and enrollment.is_active is_staff = has_access(request.user, 'staff', course_key) show_enrolled = is_enrolled or is_staff show_handouts = show_enrolled or allow_public handouts_html = get_course_info_section( request, request.user, course, 'handouts') if show_handouts else '' # TODO: TNL-7185 Legacy: Refactor to return the offer & expired data and format the message in the MFE offer_html = generate_offer_html(request.user, course_overview) course_expired_html = generate_course_expired_message( request.user, course_overview) welcome_message_html = None if get_course_tag(request.user, course_key, PREFERENCE_KEY) != 'False': if LATEST_UPDATE_FLAG.is_enabled(course_key): welcome_message_html = LatestUpdateFragmentView( ).latest_update_html(request, course) else: welcome_message_html = WelcomeMessageFragmentView( ).welcome_message_html(request, course) enroll_alert = { 'can_enroll': True, 'extra_text': None, } if not show_enrolled: if CourseMode.is_masters_only(course_key): enroll_alert['can_enroll'] = False enroll_alert['extra_text'] = _( 'Please contact your degree administrator or ' 'edX Support if you have questions.') elif course.invitation_only: enroll_alert['can_enroll'] = False course_tools = CourseToolsPluginManager.get_enabled_course_tools( request, course_key) date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=1) # User locale settings user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] dates_tab_link = request.build_absolute_uri( reverse('dates', args=[course.id])) if course_home_mfe_dates_tab_is_active(course.id): dates_tab_link = get_microfrontend_url(course_key=course.id, view_name='dates') transformers = BlockStructureTransformers() transformers += get_course_block_access_transformers(request.user) transformers += [ BlocksAPITransformer(None, None, depth=3), ] course_blocks = get_course_blocks(request.user, course_usage_key, transformers, include_completion=True) dates_widget = { 'course_date_blocks': [ block for block in date_blocks if not isinstance(block, TodaysDate) ], 'dates_tab_link': dates_tab_link, 'user_timezone': user_timezone, } data = { 'course_blocks': course_blocks, 'course_expired_html': course_expired_html, 'course_tools': course_tools, 'dates_widget': dates_widget, 'enroll_alert': enroll_alert, 'handouts_html': handouts_html, 'offer_html': offer_html, 'welcome_message_html': welcome_message_html, } context = self.get_serializer_context() context['course_key'] = course_key serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) if not course_home_mfe_progress_tab_is_active(course_key): raise Http404 # Enable NR tracing for this view based on course monitoring_utils.set_custom_attribute('course_id', course_key_string) monitoring_utils.set_custom_attribute('user_id', request.user.id) monitoring_utils.set_custom_attribute('is_staff', request.user.is_staff) _, request.user = setup_masquerade(request, course_key, staff_access=has_access( request.user, 'staff', course_key), reset_masquerade_data=True) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) enrollment_mode, _ = CourseEnrollment.enrollment_mode_for_user( request.user, course_key) # The block structure is used for both the course_grade and has_scheduled content fields # So it is called upfront and reused for optimization purposes collected_block_structure = get_block_structure_manager( course_key).get_collected() course_grade = CourseGradeFactory().read( request.user, collected_block_structure=collected_block_structure) # Get has_scheduled_content data transformers = BlockStructureTransformers() transformers += [start_date.StartDateTransformer()] usage_key = collected_block_structure.root_block_usage_key course_blocks = get_course_blocks( request.user, usage_key, transformers=transformers, collected_block_structure=collected_block_structure, include_has_scheduled_content=True) has_scheduled_content = course_blocks.get_xblock_field( usage_key, 'has_scheduled_content') # Get user_has_passing_grade data user_has_passing_grade = False if not request.user.is_anonymous: user_grade = course_grade.percent user_has_passing_grade = user_grade >= course.lowest_passing_grade descriptor = modulestore().get_course(course_key) grading_policy = descriptor.grading_policy verification_status = IDVerificationService.user_status(request.user) verification_link = None if verification_status['status'] is None or verification_status[ 'status'] == 'expired': verification_link = IDVerificationService.get_verify_location( course_id=course_key) elif verification_status['status'] == 'must_reverify': verification_link = IDVerificationService.get_verify_location( course_id=course_key) verification_data = { 'link': verification_link, 'status': verification_status['status'], 'status_date': verification_status['status_date'], } data = { 'end': course.end, 'user_has_passing_grade': user_has_passing_grade, 'certificate_data': get_cert_data(request.user, course, enrollment_mode, course_grade), 'completion_summary': get_course_blocks_completion_summary(course_key, request.user), 'course_grade': course_grade, 'has_scheduled_content': has_scheduled_content, 'section_scores': course_grade.chapter_grades.values(), 'enrollment_mode': enrollment_mode, 'grading_policy': grading_policy, 'studio_url': get_studio_url(course, 'settings/grading'), 'verification_data': verification_data, } context = self.get_serializer_context() context['staff_access'] = bool( has_access(request.user, 'staff', course)) context['course_key'] = course_key serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) # Enable NR tracing for this view based on course monitoring_utils.set_custom_attribute('course_id', course_key_string) monitoring_utils.set_custom_attribute('user_id', request.user.id) monitoring_utils.set_custom_attribute('is_staff', request.user.is_staff) _, request.user = setup_masquerade(request, course_key, staff_access=has_access( request.user, 'staff', course_key), reset_masquerade_data=True) user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] transformers = BlockStructureTransformers() transformers += course_blocks_api.get_course_block_access_transformers( request.user) transformers += [ BlocksAPITransformer(None, None, depth=3), ] course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) enrollment_mode, _ = CourseEnrollment.enrollment_mode_for_user( request.user, course_key) course_grade = CourseGradeFactory().read(request.user, course) courseware_summary = course_grade.chapter_grades.values() verification_status = IDVerificationService.user_status(request.user) verification_link = None if verification_status['status'] is None or verification_status[ 'status'] == 'expired': verification_link = IDVerificationService.get_verify_location( course_id=course_key) elif verification_status['status'] == 'must_reverify': verification_link = IDVerificationService.get_verify_location( course_id=course_key) verification_data = { 'link': verification_link, 'status': verification_status['status'], 'status_date': verification_status['status_date'], } data = { 'certificate_data': get_cert_data(request.user, course, enrollment_mode, course_grade), 'courseware_summary': courseware_summary, 'credit_course_requirements': credit_course_requirements(course_key, request.user), 'credit_support_url': CREDIT_SUPPORT_URL, 'enrollment_mode': enrollment_mode, 'studio_url': get_studio_url(course, 'settings/grading'), 'user_timezone': user_timezone, 'verification_data': verification_data, } context = self.get_serializer_context() context['staff_access'] = bool( has_access(request.user, 'staff', course)) context['course_key'] = course_key serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) student_id = kwargs.get('student_id') if not course_home_mfe_progress_tab_is_active(course_key): raise Http404 # Enable NR tracing for this view based on course monitoring_utils.set_custom_attribute('course_id', course_key_string) monitoring_utils.set_custom_attribute('user_id', request.user.id) monitoring_utils.set_custom_attribute('is_staff', request.user.is_staff) is_staff = bool(has_access(request.user, 'staff', course_key)) student = self._get_student_user(request, course_key, student_id, is_staff) username = get_enterprise_learner_generic_name( request) or student.username course = get_course_with_access(student, 'load', course_key, check_if_enrolled=False) course_overview = CourseOverview.get_from_id(course_key) enrollment = CourseEnrollment.get_enrollment(student, course_key) enrollment_mode = getattr(enrollment, 'mode', None) if not (enrollment and enrollment.is_active) and not is_staff: return Response('User not enrolled.', status=401) # The block structure is used for both the course_grade and has_scheduled content fields # So it is called upfront and reused for optimization purposes collected_block_structure = get_block_structure_manager( course_key).get_collected() course_grade = CourseGradeFactory().read( student, collected_block_structure=collected_block_structure) # recalculate course grade from visible grades (stored grade was calculated over all grades, visible or not) course_grade.update(visible_grades_only=True, has_staff_access=is_staff) # Get has_scheduled_content data transformers = BlockStructureTransformers() transformers += [ start_date.StartDateTransformer(), ContentTypeGateTransformer() ] usage_key = collected_block_structure.root_block_usage_key course_blocks = get_course_blocks( student, usage_key, transformers=transformers, collected_block_structure=collected_block_structure, include_has_scheduled_content=True) has_scheduled_content = course_blocks.get_xblock_field( usage_key, 'has_scheduled_content') # Get user_has_passing_grade data user_has_passing_grade = False if not student.is_anonymous: user_grade = course_grade.percent user_has_passing_grade = user_grade >= course.lowest_passing_grade descriptor = modulestore().get_course(course_key) grading_policy = descriptor.grading_policy verification_status = IDVerificationService.user_status(student) verification_link = None if verification_status['status'] is None or verification_status[ 'status'] == 'expired': verification_link = IDVerificationService.get_verify_location( course_id=course_key) elif verification_status['status'] == 'must_reverify': verification_link = IDVerificationService.get_verify_location( course_id=course_key) verification_data = { 'link': verification_link, 'status': verification_status['status'], 'status_date': verification_status['status_date'], } access_expiration = get_access_expiration_data(request.user, course_overview) data = { 'access_expiration': access_expiration, 'certificate_data': get_cert_data(student, course, enrollment_mode, course_grade), 'completion_summary': get_course_blocks_completion_summary(course_key, student), 'course_grade': course_grade, 'credit_course_requirements': credit_course_requirements(course_key, student), 'end': course.end, 'enrollment_mode': enrollment_mode, 'grading_policy': grading_policy, 'has_scheduled_content': has_scheduled_content, 'section_scores': list(course_grade.chapter_grades.values()), 'studio_url': get_studio_url(course, 'settings/grading'), 'username': username, 'user_has_passing_grade': user_has_passing_grade, 'verification_data': verification_data, } context = self.get_serializer_context() context['staff_access'] = is_staff context['course_blocks'] = course_blocks context['course_key'] = course_key # course_overview and enrollment will be used by VerifiedModeSerializer context['course_overview'] = course_overview context['enrollment'] = enrollment serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) student_id = kwargs.get('student_id') if student_id: try: student_id = int(student_id) except ValueError: raise Http404 if not course_home_mfe_progress_tab_is_active(course_key): raise Http404 # Enable NR tracing for this view based on course monitoring_utils.set_custom_attribute('course_id', course_key_string) monitoring_utils.set_custom_attribute('user_id', request.user.id) monitoring_utils.set_custom_attribute('is_staff', request.user.is_staff) is_staff = bool(has_access(request.user, 'staff', course_key)) if student_id is None or student_id == request.user.id: _, student = setup_masquerade(request, course_key, staff_access=is_staff, reset_masquerade_data=True) else: # When a student_id is passed in, we display the progress page for the user # with the provided user id, rather than the requesting user try: coach_access = has_ccx_coach_role(request.user, course_key) except CCXLocatorValidationException: coach_access = False has_access_on_students_profiles = is_staff or coach_access # Requesting access to a different student's profile if not has_access_on_students_profiles: raise Http404 try: student = User.objects.get(id=student_id) except User.DoesNotExist as exc: raise Http404 from exc username = get_enterprise_learner_generic_name( request) or student.username course = get_course_with_access(student, 'load', course_key, check_if_enrolled=False) course_overview = CourseOverview.get_from_id(course_key) enrollment = CourseEnrollment.get_enrollment(student, course_key) enrollment_mode = getattr(enrollment, 'mode', None) if not (enrollment and enrollment.is_active) and not is_staff: return Response('User not enrolled.', status=401) # The block structure is used for both the course_grade and has_scheduled content fields # So it is called upfront and reused for optimization purposes collected_block_structure = get_block_structure_manager( course_key).get_collected() course_grade = CourseGradeFactory().read( student, collected_block_structure=collected_block_structure) # Get has_scheduled_content data transformers = BlockStructureTransformers() transformers += [ start_date.StartDateTransformer(), ContentTypeGateTransformer() ] usage_key = collected_block_structure.root_block_usage_key course_blocks = get_course_blocks( student, usage_key, transformers=transformers, collected_block_structure=collected_block_structure, include_has_scheduled_content=True) has_scheduled_content = course_blocks.get_xblock_field( usage_key, 'has_scheduled_content') # Get user_has_passing_grade data user_has_passing_grade = False if not student.is_anonymous: user_grade = course_grade.percent user_has_passing_grade = user_grade >= course.lowest_passing_grade descriptor = modulestore().get_course(course_key) grading_policy = descriptor.grading_policy verification_status = IDVerificationService.user_status(student) verification_link = None if verification_status['status'] is None or verification_status[ 'status'] == 'expired': verification_link = IDVerificationService.get_verify_location( course_id=course_key) elif verification_status['status'] == 'must_reverify': verification_link = IDVerificationService.get_verify_location( course_id=course_key) verification_data = { 'link': verification_link, 'status': verification_status['status'], 'status_date': verification_status['status_date'], } data = { 'username': username, 'end': course.end, 'user_has_passing_grade': user_has_passing_grade, 'certificate_data': get_cert_data(student, course, enrollment_mode, course_grade), 'completion_summary': get_course_blocks_completion_summary(course_key, student), 'course_grade': course_grade, 'has_scheduled_content': has_scheduled_content, 'section_scores': course_grade.chapter_grades.values(), 'enrollment_mode': enrollment_mode, 'grading_policy': grading_policy, 'studio_url': get_studio_url(course, 'settings/grading'), 'verification_data': verification_data, } context = self.get_serializer_context() context['staff_access'] = is_staff context['course_blocks'] = course_blocks context['course_key'] = course_key # course_overview and enrollment will be used by VerifiedModeSerializerMixin context['course_overview'] = course_overview context['enrollment'] = enrollment serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def get_blocks( request, usage_key, user=None, depth=None, nav_depth=None, requested_fields=None, block_counts=None, student_view_data=None, return_type='dict', block_types_filter=None, ): """ Return a serialized representation of the course blocks. Arguments: request (HTTPRequest): Used for calling django reverse. usage_key (UsageKey): Identifies the starting block of interest. user (User): Optional user object for whom the blocks are being retrieved. If None, blocks are returned regardless of access checks. depth (integer or None): Identifies the depth of the tree to return starting at the root block. If None, the entire tree starting at the root is returned. nav_depth (integer): Optional parameter that indicates how far deep to traverse into the block hierarchy before bundling all the descendants for navigation. requested_fields (list): Optional list of names of additional fields to return for each block. Supported fields are listed in transformers.SUPPORTED_FIELDS. block_counts (list): Optional list of names of block types for which to return an aggregate count of blocks. student_view_data (list): Optional list of names of block types for which blocks to return their student_view_data. return_type (string): Possible values are 'dict' or 'list'. Indicates the format for returning the blocks. block_types_filter (list): Optional list of block type names used to filter the final result of returned blocks. """ # create ordered list of transformers, adding BlocksAPITransformer at end. transformers = BlockStructureTransformers() if requested_fields is None: requested_fields = [] include_completion = 'completion' in requested_fields include_special_exams = 'special_exam_info' in requested_fields include_gated_sections = 'show_gated_sections' in requested_fields if user is not None: transformers += course_blocks_api.get_course_block_access_transformers( user) transformers += [ MilestonesAndSpecialExamsTransformer( include_special_exams=include_special_exams, include_gated_sections=include_gated_sections) ] transformers += [HiddenContentTransformer()] transformers += [ BlocksAPITransformer(block_counts, student_view_data, depth, nav_depth) ] if include_completion: transformers += [BlockCompletionTransformer()] # transform blocks = course_blocks_api.get_course_blocks(user, usage_key, transformers) # filter blocks by types if block_types_filter: block_keys_to_remove = [] for block_key in blocks: block_type = blocks.get_xblock_field(block_key, 'category') if block_type not in block_types_filter: block_keys_to_remove.append(block_key) for block_key in block_keys_to_remove: blocks.remove_block(block_key, keep_descendants=True) # serialize serializer_context = { 'request': request, 'block_structure': blocks, 'requested_fields': requested_fields or [], } if return_type == 'dict': serializer = BlockDictSerializer(blocks, context=serializer_context, many=False) else: serializer = BlockSerializer(blocks, context=serializer_context, many=True) # return serialized data return serializer.data
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) course_usage_key = modulestore().make_course_usage_key(course_key) # Enable NR tracing for this view based on course monitoring_utils.set_custom_metric('course_id', course_key_string) monitoring_utils.set_custom_metric('user_id', request.user.id) monitoring_utils.set_custom_metric('is_staff', request.user.is_staff) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) _, request.user = setup_masquerade( request, course_key, staff_access=has_access(request.user, 'staff', course_key), reset_masquerade_data=True, ) enrollment = CourseEnrollment.get_enrollment(request.user, course_key) allow_anonymous = COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(course_key) allow_public = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC is_enrolled = enrollment and enrollment.is_active is_staff = has_access(request.user, 'staff', course_key) show_handouts = is_enrolled or is_staff or allow_public handouts_html = get_course_info_section(request, request.user, course, 'handouts') if show_handouts else '' welcome_message_html = None if get_course_tag(request.user, course_key, PREFERENCE_KEY) != 'False': if LATEST_UPDATE_FLAG.is_enabled(course_key): welcome_message_html = LatestUpdateFragmentView().latest_update_html(request, course) else: welcome_message_html = WelcomeMessageFragmentView().welcome_message_html(request, course) course_tools = CourseToolsPluginManager.get_enabled_course_tools(request, course_key) date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=1) # User locale settings user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] dates_tab_link = request.build_absolute_uri(reverse('dates', args=[course.id])) if course_home_mfe_dates_tab_is_active(course.id): dates_tab_link = get_microfrontend_url(course_key=course.id, view_name='dates') transformers = BlockStructureTransformers() transformers += get_course_block_access_transformers(request.user) transformers += [ BlocksAPITransformer(None, None, depth=3), ] course_blocks = get_course_blocks(request.user, course_usage_key, transformers, include_completion=True) dates_widget = { 'course_date_blocks': [block for block in date_blocks if not isinstance(block, TodaysDate)], 'dates_tab_link': dates_tab_link, 'user_timezone': user_timezone, } data = { 'course_blocks': course_blocks, 'course_tools': course_tools, 'dates_widget': dates_widget, 'handouts_html': handouts_html, 'welcome_message_html': welcome_message_html, } context = self.get_serializer_context() context['course_key'] = course_key serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def get_blocks( request, usage_key, user=None, depth=None, nav_depth=None, requested_fields=None, block_counts=None, student_view_data=None, return_type='dict', block_types_filter=None, hide_access_denials=False, allow_start_dates_in_future=False, ): """ Return a serialized representation of the course blocks. Arguments: request (HTTPRequest): Used for calling django reverse. usage_key (UsageKey): Identifies the starting block of interest. user (User): Optional user object for whom the blocks are being retrieved. If None, blocks are returned regardless of access checks. depth (integer or None): Identifies the depth of the tree to return starting at the root block. If None, the entire tree starting at the root is returned. nav_depth (integer): Optional parameter that indicates how far deep to traverse into the block hierarchy before bundling all the descendants for navigation. requested_fields (list): Optional list of names of additional fields to return for each block. Supported fields are listed in transformers.SUPPORTED_FIELDS. block_counts (list): Optional list of names of block types for which to return an aggregate count of blocks. student_view_data (list): Optional list of names of block types for which blocks to return their student_view_data. return_type (string): Possible values are 'dict' or 'list'. Indicates the format for returning the blocks. block_types_filter (list): Optional list of block type names used to filter the final result of returned blocks. hide_access_denials (bool): When True, filter out any blocks that were denied access to the user, even if they have access denial messages attached. allow_start_dates_in_future (bool): When True, will allow blocks to be returned that can bypass the StartDateTransformer's filter to show blocks with start dates in the future. """ if HIDE_ACCESS_DENIALS_FLAG.is_enabled(): hide_access_denials = True # create ordered list of transformers, adding BlocksAPITransformer at end. transformers = BlockStructureTransformers() if requested_fields is None: requested_fields = [] include_completion = 'completion' in requested_fields include_effort_estimation = (EffortEstimationTransformer.EFFORT_TIME in requested_fields or EffortEstimationTransformer.EFFORT_ACTIVITIES in requested_fields) include_gated_sections = 'show_gated_sections' in requested_fields include_has_scheduled_content = 'has_scheduled_content' in requested_fields include_special_exams = 'special_exam_info' in requested_fields if user is not None: transformers += course_blocks_api.get_course_block_access_transformers(user) transformers += [ MilestonesAndSpecialExamsTransformer( include_special_exams=include_special_exams, include_gated_sections=include_gated_sections ), HiddenContentTransformer() ] # Note: A change to the BlockCompletionTransformer (https://github.com/edx/edx-platform/pull/27622/) # will be introducing a bug if hide_access_denials is True. I'm accepting this risk because in # the AccessDeniedMessageFilterTransformer, there is note about deleting it and I believe it is # technically deprecated functionality. The only use case where hide_access_denials is True # (outside of explicitly setting the temporary waffle flag) is in lms/djangoapps/course_api/blocks/urls.py # for a v1 api that I also believe should have been deprecated and removed. When this code is removed, # please also remove this comment. Thanks! if hide_access_denials: transformers += [AccessDeniedMessageFilterTransformer()] if include_effort_estimation: transformers += [EffortEstimationTransformer()] transformers += [ BlocksAPITransformer( block_counts, student_view_data, depth, nav_depth ) ] # transform blocks = course_blocks_api.get_course_blocks( user, usage_key, transformers, allow_start_dates_in_future=allow_start_dates_in_future, include_completion=include_completion, include_has_scheduled_content=include_has_scheduled_content ) # filter blocks by types if block_types_filter: block_keys_to_remove = [] for block_key in blocks: block_type = blocks.get_xblock_field(block_key, 'category') if block_type not in block_types_filter: block_keys_to_remove.append(block_key) for block_key in block_keys_to_remove: blocks.remove_block(block_key, keep_descendants=True) # serialize serializer_context = { 'request': request, 'block_structure': blocks, 'requested_fields': requested_fields or [], } if return_type == 'dict': serializer = BlockDictSerializer(blocks, context=serializer_context, many=False) else: serializer = BlockSerializer(blocks, context=serializer_context, many=True) # return serialized data return serializer.data
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) course_usage_key = modulestore().make_course_usage_key(course_key) # Enable NR tracing for this view based on course monitoring_utils.set_custom_metric('course_id', course_key_string) monitoring_utils.set_custom_metric('user_id', request.user.id) monitoring_utils.set_custom_metric('is_staff', request.user.is_staff) course_tools = CourseToolsPluginManager.get_enabled_course_tools( request, course_key) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=1) # User locale settings user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] dates_tab_link = request.build_absolute_uri( reverse('dates', args=[course.id])) if course_home_mfe_dates_tab_is_active(course.id): dates_tab_link = get_microfrontend_url(course_key=course.id, view_name='dates') transformers = BlockStructureTransformers() transformers += course_blocks_api.get_course_block_access_transformers( request.user) transformers += [ BlocksAPITransformer(None, None, depth=3), ] course_blocks = get_course_blocks(request.user, course_usage_key, transformers, include_completion=True) dates_widget = { 'course_date_blocks': [ block for block in date_blocks if not isinstance(block, TodaysDate) ], 'dates_tab_link': dates_tab_link, 'user_timezone': user_timezone, } data = { 'course_tools': course_tools, 'course_blocks': course_blocks, 'dates_widget': dates_widget, } context = self.get_serializer_context() context['course_key'] = course_key serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)