def render_to_fragment(self, request, course_id=None, **kwargs): """ Render the course dates fragment. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) course_date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=1) # We will use this boolean to gate if we show a link to the dates tab. This same logic # dictates if we show the tab at all. user_enrolled = ( request.user and request.user.is_authenticated and (bool( CourseEnrollment.is_enrolled(request.user, course.id) or has_access(request.user, 'staff', course, course.id)))) context = { 'course_date_blocks': [ block for block in course_date_blocks if block.title != 'current_datetime' ], 'dates_tab_link': reverse('dates', args=[course.id]), 'user_enrolled': user_enrolled, } html = render_to_string(self.template_name, context) dates_fragment = Fragment(html) self.add_fragment_resource_urls(dates_fragment) return dates_fragment
def render_to_fragment(self, request, course_id=None, **kwargs): """ Render the course dates fragment. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) course_date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=2) context = { 'course_date_blocks': [ block for block in course_date_blocks if block.title != 'current_datetime' ] } html = render_to_string(self.template_name, context) dates_fragment = Fragment(html) self.add_fragment_resource_urls(dates_fragment) return dates_fragment
def test_dates_with_openassessments(self, rubric_assessments, date_block_count): course = create_self_paced_course_run(days_till_start=-1, org_id='TestOrg') user = create_user() request = self.make_request(user) CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED) now = datetime.now(utc) chapter = ItemFactory.create( parent=course, category="chapter", graded=True, ) section = ItemFactory.create( parent=chapter, category="sequential", ) vertical = ItemFactory.create( parent=section, category="vertical", ) ItemFactory.create( parent=vertical, category="openassessment", rubric_assessments=rubric_assessments, submission_start=(now + timedelta(days=1)).isoformat(), submission_end=(now + timedelta(days=7)).isoformat(), ) blocks = get_course_date_blocks(course, user, request, include_past_dates=True) assert len(blocks) == date_block_count
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) if course_home_legacy_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) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) is_staff = bool(has_access(request.user, 'staff', course_key)) _, request.user = setup_masquerade( request, course_key, staff_access=is_staff, reset_masquerade_data=True, ) if not CourseEnrollment.is_enrolled(request.user, course_key) and not is_staff: return Response('User not enrolled.', status=401) blocks = get_course_date_blocks(course, request.user, request, include_access=True, include_past_dates=True) learner_is_full_access = not ContentTypeGatingConfig.enabled_for_enrollment( user=request.user, course_key=course_key, ) # User locale settings user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] data = { 'has_ended': course.has_ended(), 'course_date_blocks': [block for block in blocks if not isinstance(block, TodaysDate)], 'learner_is_full_access': learner_is_full_access, 'user_timezone': user_timezone, } context = self.get_serializer_context() context['learner_is_full_access'] = learner_is_full_access serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def render_to_fragment(self, request, course_id=None, **kwargs): # lint-amnesty, pylint: disable=arguments-differ """ Render the course dates fragment. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) course_date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=1) dates_tab_enabled = DatesTab.is_enabled(course, request.user) dates_tab_link = get_learning_mfe_home_url(course_key=course.id, url_fragment='dates') context = { 'course_date_blocks': [ block for block in course_date_blocks if block.title != 'current_datetime' ], 'dates_tab_link': dates_tab_link, 'dates_tab_enabled': dates_tab_enabled, } html = render_to_string(self.template_name, context) dates_fragment = Fragment(html) self.add_fragment_resource_urls(dates_fragment) return dates_fragment
def render_to_fragment(self, request, course_id, user_access, **kwargs): """ Renders a course message fragment for the specified course. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key) # Get time until the start date, if already started, or no start date, value will be zero or negative now = datetime.now(UTC) already_started = course.start and now > course.start days_until_start_string = "started" if already_started else format_timedelta( course.start - now, locale=to_locale(get_language()) ) course_start_data = { 'course_start_date': format_date(course.start, locale=to_locale(get_language())), 'already_started': already_started, 'days_until_start_string': days_until_start_string } # Register the course home messages to be loaded on the page _register_course_home_messages(request, course, user_access, course_start_data) # Register course date alerts for course_date_block in get_course_date_blocks(course, request.user, request): course_date_block.register_alerts(request, course) # Register a course goal message, if appropriate # Only show the set course goal message for enrolled, unverified # users that have not yet set a goal in a course that allows for # verified statuses. user_goal = get_course_goal(auth.get_user(request), course_key) is_already_verified = CourseEnrollment.is_enrolled_as_verified(request.user, course_key) if has_course_goal_permission(request, course_id, user_access) and not is_already_verified and not user_goal: _register_course_goal_message(request, course) # Grab the relevant messages course_home_messages = list(CourseHomeMessages.user_messages(request)) # Pass in the url used to set a course goal goal_api_url = get_goal_api_url(request) # Grab the logo image_src = 'course_experience/images/home_message_author.png' context = { 'course_home_messages': course_home_messages, 'goal_api_url': goal_api_url, 'image_src': image_src, 'course_id': course_id, 'username': request.user.username, } html = render_to_string('course_experience/course-messages-fragment.html', context) return Fragment(html)
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_dates_tab_is_active(course_key): return Response(status=status.HTTP_404_NOT_FOUND) # 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) blocks = get_course_date_blocks(course, request.user, request, include_access=True, include_past_dates=True) missed_deadlines, missed_gated_content = dates_banner_should_display( course_key, request.user) learner_is_full_access = not ContentTypeGatingConfig.enabled_for_enrollment( user=request.user, course_key=course_key, ) # User locale settings user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] data = { 'has_ended': course.has_ended(), 'course_date_blocks': [block for block in blocks if not isinstance(block, TodaysDate)], 'missed_deadlines': missed_deadlines, 'missed_gated_content': missed_gated_content, 'learner_is_full_access': learner_is_full_access, 'user_timezone': user_timezone, 'verified_upgrade_link': verified_upgrade_deadline_link(request.user, course=course), } context = self.get_serializer_context() context['learner_is_full_access'] = learner_is_full_access serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def render_to_fragment(self, request, course_id=None, **kwargs): """ Render the course dates fragment. """ from lms.urls import RESET_COURSE_DEADLINES_NAME from openedx.features.course_experience.urls import COURSE_DATES_FRAGMENT_VIEW_NAME course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) course_date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=2) display_reset_dates_banner = False if RELATIVE_DATES_FLAG.is_enabled(course.id): display_reset_dates_banner = reset_deadlines_banner_should_display( course_key, request) reset_deadlines_url = reverse(RESET_COURSE_DEADLINES_NAME ) if display_reset_dates_banner else None reset_deadlines_redirect_url_base = COURSE_DATES_FRAGMENT_VIEW_NAME if ( reset_deadlines_url) else None context = { 'course_date_blocks': [ block for block in course_date_blocks if block.title != 'current_datetime' ], 'display_reset_dates_banner': display_reset_dates_banner, 'reset_deadlines_url': reset_deadlines_url, 'reset_deadlines_redirect_url_base': reset_deadlines_redirect_url_base, 'reset_deadlines_redirect_url_id_dict': { 'course_id': course_id } } html = render_to_string(self.template_name, context) dates_fragment = Fragment(html) self.add_fragment_resource_urls(dates_fragment) return dates_fragment
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') # 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_key = CourseKey.from_string(course_key_string) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) blocks = get_course_date_blocks(course, request.user, request, include_access=True, include_past_dates=True) display_reset_dates_text, _ = dates_banner_should_display( course_key, request) learner_is_verified = False enrollment = get_enrollment(request.user.username, course_key_string) if enrollment: learner_is_verified = enrollment.get('mode') == 'verified' # User locale settings user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] data = { 'course_date_blocks': [block for block in blocks if not isinstance(block, TodaysDate)], 'display_reset_dates_text': display_reset_dates_text, 'learner_is_verified': learner_is_verified, 'user_timezone': user_timezone, 'verified_upgrade_link': verified_upgrade_deadline_link(request.user, course=course), } context = self.get_serializer_context() context['learner_is_verified'] = learner_is_verified serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def render_to_fragment(self, request, course_id=None, **kwargs): """ Render the course dates fragment. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) course_date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=1) dates_tab_enabled = DatesTab.is_enabled(course, request.user) if course_home_mfe_dates_tab_is_active(course_key): dates_tab_link = get_microfrontend_url(course_key=course.id, view_name='dates') else: dates_tab_link = reverse('dates', args=[course.id]) context = { 'course_date_blocks': [ block for block in course_date_blocks if block.title != 'current_datetime' ], 'dates_tab_link': dates_tab_link, 'dates_tab_enabled': dates_tab_enabled, } html = render_to_string(self.template_name, context) dates_fragment = Fragment(html) self.add_fragment_resource_urls(dates_fragment) return dates_fragment
def assert_block_types(self, course, user, expected_blocks): """Assert that the enabled block types for this course are as expected.""" blocks = get_course_date_blocks(course, user) assert len(blocks) == len(expected_blocks) assert {type(b) for b in blocks} == set(expected_blocks)
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) 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 assert_block_types(self, course, user, expected_blocks): """Assert that the enabled block types for this course are as expected.""" blocks = get_course_date_blocks(course, user) self.assertEqual(len(blocks), len(expected_blocks)) self.assertEqual(set(type(b) for b in blocks), set(expected_blocks))
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)
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 course_home_legacy_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) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) masquerade_object, request.user = setup_masquerade( request, course_key, staff_access=has_access(request.user, 'staff', course_key), reset_masquerade_data=True, ) user_is_masquerading = is_masquerading( request.user, course_key, course_masquerade=masquerade_object) 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 allow_public_outline = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC_OUTLINE # User locale settings user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] if course_home_legacy_is_active(course.id): dates_tab_link = request.build_absolute_uri( reverse('dates', args=[course.id])) else: dates_tab_link = get_learning_mfe_home_url(course_key=course.id, view_name='dates') # Set all of the defaults access_expiration = None cert_data = None course_blocks = None course_goals = {'goal_options': [], 'selected_goal': None} course_tools = CourseToolsPluginManager.get_enabled_course_tools( request, course_key) dates_widget = { 'course_date_blocks': [], 'dates_tab_link': dates_tab_link, 'user_timezone': user_timezone, } enroll_alert = { 'can_enroll': True, 'extra_text': None, } handouts_html = None offer_data = None resume_course = { 'has_visited_course': False, 'url': None, } welcome_message_html = None is_enrolled = enrollment and enrollment.is_active is_staff = bool(has_access(request.user, 'staff', course_key)) show_enrolled = is_enrolled or is_staff if show_enrolled: course_blocks = get_course_outline_block_tree( request, course_key_string, request.user) date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=1) dates_widget['course_date_blocks'] = [ block for block in date_blocks if not isinstance(block, TodaysDate) ] handouts_html = get_course_info_section(request, request.user, course, 'handouts') welcome_message_html = get_current_update_for_user(request, course) offer_data = generate_offer_data(request.user, course_overview) access_expiration = get_access_expiration_data( request.user, course_overview) cert_data = get_cert_data(request.user, course, enrollment.mode) if is_enrolled else None # Only show the set course goal message for enrolled, unverified # users in a course that allows for verified statuses. is_already_verified = CourseEnrollment.is_enrolled_as_verified( request.user, course_key) if not is_already_verified and has_course_goal_permission( request, course_key_string, {'is_enrolled': is_enrolled}): course_goals = { 'goal_options': valid_course_goals_ordered(include_unsure=True), 'selected_goal': None } selected_goal = get_course_goal(request.user, course_key) if selected_goal: course_goals['selected_goal'] = { 'key': selected_goal.goal_key, 'text': get_course_goal_text(selected_goal.goal_key), } try: resume_block = get_key_to_last_completed_block( request.user, course.id) resume_course['has_visited_course'] = True resume_path = reverse('jump_to', kwargs={ 'course_id': course_key_string, 'location': str(resume_block) }) resume_course['url'] = request.build_absolute_uri(resume_path) except UnavailableCompletionData: start_block = get_start_block(course_blocks) resume_course['url'] = start_block['lms_web_url'] elif allow_public_outline or allow_public or user_is_masquerading: course_blocks = get_course_outline_block_tree( request, course_key_string, None) if allow_public or user_is_masquerading: handouts_html = get_course_info_section( request, request.user, course, 'handouts') 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 # Sometimes there are sequences returned by Course Blocks that we # don't actually want to show to the user, such as when a sequence is # composed entirely of units that the user can't access. The Learning # Sequences API knows how to roll this up, so we use it determine which # sequences we should remove from course_blocks. # # The long term goal is to remove the Course Blocks API call entirely, # so this is a tiny first step in that migration. if course_blocks and learning_sequences_api_available( course_key, request.user): user_course_outline = get_user_course_outline( course_key, request.user, datetime.now(tz=timezone.utc)) available_seq_ids = { str(usage_key) for usage_key in user_course_outline.sequences } # course_blocks is a reference to the root of the course, so we go # through the chapters (sections) to look for sequences to remove. for chapter_data in course_blocks['children']: chapter_data['children'] = [ seq_data for seq_data in chapter_data['children'] if (seq_data['id'] in available_seq_ids or # Edge case: Sometimes we have weird course structures. # We expect only sequentials here, but if there is # another type, just skip it (don't filter it out). seq_data['type'] != 'sequential') ] if 'children' in chapter_data else [] data = { 'access_expiration': access_expiration, 'cert_data': cert_data, 'course_blocks': course_blocks, 'course_goals': course_goals, 'course_tools': course_tools, 'dates_widget': dates_widget, 'enroll_alert': enroll_alert, 'handouts_html': handouts_html, 'has_ended': course.has_ended(), 'offer': offer_data, 'resume_course': resume_course, 'welcome_message_html': welcome_message_html, } context = self.get_serializer_context() context['course_overview'] = course_overview context['enable_links'] = show_enrolled or allow_public 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) 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_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) 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 allow_public_outline = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC_OUTLINE is_enrolled = enrollment and enrollment.is_active is_staff = bool(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 = show_enrolled and generate_offer_html( request.user, course_overview) course_expired_html = show_enrolled and generate_course_expired_message( request.user, course_overview) welcome_message_html = None if show_enrolled: if LATEST_UPDATE_FLAG.is_enabled(course_key): welcome_message_html = LatestUpdateFragmentView( ).latest_update_html(request, course) elif get_course_tag(request.user, course_key, PREFERENCE_KEY) != 'False': 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') course_blocks = None if show_enrolled or allow_public or allow_public_outline: outline_user = request.user if show_enrolled else None course_blocks = get_course_outline_block_tree( request, course_key_string, outline_user) resume_course = { 'has_visited_course': False, 'url': None, } if show_enrolled: try: resume_block = get_key_to_last_completed_block( request.user, course.id) resume_course['has_visited_course'] = True except UnavailableCompletionData: resume_block = course_usage_key resume_path = reverse('jump_to', kwargs={ 'course_id': course_key_string, 'location': str(resume_block) }) resume_course['url'] = request.build_absolute_uri(resume_path) 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, } # Only show the set course goal message for enrolled, unverified # users in a course that allows for verified statuses. is_already_verified = CourseEnrollment.is_enrolled_as_verified( request.user, course_key) if (not is_already_verified and has_course_goal_permission( request, course_key_string, {'is_enrolled': is_enrolled})): course_goals = { 'goal_options': valid_course_goals_ordered(include_unsure=True), 'selected_goal': None } selected_goal = get_course_goal(request.user, course_key) if selected_goal: course_goals['selected_goal'] = { 'key': selected_goal.goal_key, 'text': get_course_goal_text(selected_goal.goal_key), } else: course_goals = {'goal_options': [], 'selected_goal': None} data = { 'course_blocks': course_blocks, 'course_expired_html': course_expired_html or None, 'course_goals': course_goals, 'course_tools': course_tools, 'dates_widget': dates_widget, 'enroll_alert': enroll_alert, 'handouts_html': handouts_html or None, 'has_ended': course.has_ended(), 'offer_html': offer_html or None, 'resume_course': resume_course, 'welcome_message_html': welcome_message_html or None, } context = self.get_serializer_context() context['course_key'] = course_key context['enable_links'] = show_enrolled or allow_public serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def test_enabled_block_types_with_assignments(self): # pylint: disable=too-many-statements """ Creates a course with multiple subsections to test all of the different cases for assignment dates showing up. Mocks out calling the edx-when service and then validates the correct data is set and returned. """ course = create_course_run(days_till_start=-100) user = create_user() request = self.make_request(user) CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED) now = datetime.now(utc) assignment_title_html = ['<a href=', '</a>'] with self.store.bulk_operations(course.id): section = ItemFactory.create(category='chapter', parent_location=course.location) ItemFactory.create( category='sequential', display_name='Released', parent_location=section.location, start=now - timedelta(days=1), due=now + timedelta(days=6), graded=True, format='Homework', ) ItemFactory.create( category='sequential', display_name='Not released', parent_location=section.location, start=now + timedelta(days=1), due=now + timedelta(days=7), graded=True, format='Homework', ) ItemFactory.create( category='sequential', display_name='Third nearest assignment', parent_location=section.location, start=now + timedelta(days=1), due=now + timedelta(days=8), graded=True, format='Exam', ) ItemFactory.create( category='sequential', display_name='Past due date', parent_location=section.location, start=now - timedelta(days=14), due=now - timedelta(days=7), graded=True, format='Exam', ) ItemFactory.create( category='sequential', display_name='Not returned since we do not get non-graded subsections', parent_location=section.location, start=now + timedelta(days=1), due=now - timedelta(days=7), graded=False, ) ItemFactory.create( category='sequential', display_name='No start date', parent_location=section.location, start=None, due=now + timedelta(days=9), graded=True, format='Speech', ) ItemFactory.create( category='sequential', # Setting display name to None should set the assignment title to 'Assignment' display_name=None, parent_location=section.location, start=now - timedelta(days=14), due=now + timedelta(days=10), graded=True, format=None, ) dummy_subsection = ItemFactory.create(category='sequential', graded=True, due=now + timedelta(days=11)) # We are deleting this subsection right after creating it because we need to pass in a real # location object (dummy_subsection.location), but do not want this to exist inside of the modulestore with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, course.id): self.store.delete_item(dummy_subsection.location, user.id) # Standard widget case where we restrict the number of assignments. expected_blocks = ( TodaysDate, CourseAssignmentDate, CourseAssignmentDate, CourseEndDate, VerificationDeadlineDate ) blocks = get_course_date_blocks(course, user, request, num_assignments=2) assert len(blocks) == len(expected_blocks) assert {type(b) for b in blocks} == set(expected_blocks) assignment_blocks = filter( lambda b: isinstance(b, CourseAssignmentDate), blocks ) for assignment in assignment_blocks: assignment_title = str(assignment.title_html) or str(assignment.title) assert assignment_title != 'Third nearest assignment' assert assignment_title != 'Past due date' assert assignment_title != 'Not returned since we do not get non-graded subsections' # checking if it is _in_ the title instead of being the title since released assignments # are actually links. Unreleased assignments are just the string of the title. if 'Released' in assignment_title: for html_tag in assignment_title_html: assert html_tag in assignment_title elif assignment_title == 'Not released': for html_tag in assignment_title_html: assert html_tag not in assignment_title # No restrictions on number of assignments to return expected_blocks = ( CourseStartDate, TodaysDate, CourseAssignmentDate, CourseAssignmentDate, CourseAssignmentDate, CourseAssignmentDate, CourseAssignmentDate, CourseAssignmentDate, CourseEndDate, VerificationDeadlineDate ) blocks = get_course_date_blocks(course, user, request, include_past_dates=True) assert len(blocks) == len(expected_blocks) assert {type(b) for b in blocks} == set(expected_blocks) assignment_blocks = filter( lambda b: isinstance(b, CourseAssignmentDate), blocks ) for assignment in assignment_blocks: assignment_title = str(assignment.title_html) or str(assignment.title) assert assignment_title != 'Not returned since we do not get non-graded subsections' assignment_type = str(assignment.assignment_type) # checking if it is _in_ the title instead of being the title since released assignments # are actually links. Unreleased assignments are just the string of the title. # also checking that the assignment type is returned for graded subsections if 'Released' in assignment_title: assert assignment_type == 'Homework' for html_tag in assignment_title_html: assert html_tag in assignment_title elif assignment_title == 'Not released': assert assignment_type == 'Homework' for html_tag in assignment_title_html: assert html_tag not in assignment_title elif assignment_title == 'Third nearest assignment': assert assignment_type == 'Exam' # It's still not released for html_tag in assignment_title_html: assert html_tag not in assignment_title elif 'Past due date' in assignment_title: assert now > assignment.date assert assignment_type == 'Exam' for html_tag in assignment_title_html: assert html_tag in assignment_title elif 'No start date' == assignment_title: assert assignment_type == 'Speech' # Can't determine if it is released so it does not get a link for html_tag in assignment_title_html: assert html_tag not in assignment_title # This is the item with no display name where we set one ourselves. elif 'Assignment' in assignment_title: assert assignment_type is None # Can't determine if it is released so it does not get a link for html_tag in assignment_title_html: assert html_tag in assignment_title
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_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) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) masquerade_object, request.user = setup_masquerade( request, course_key, staff_access=has_access(request.user, 'staff', course_key), reset_masquerade_data=True, ) user_is_masquerading = is_masquerading( request.user, course_key, course_masquerade=masquerade_object) 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 allow_public_outline = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC_OUTLINE # 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_learning_mfe_home_url(course_key=course.id, view_name='dates') # Set all of the defaults access_expiration = None course_blocks = None course_goals = {'goal_options': [], 'selected_goal': None} course_tools = CourseToolsPluginManager.get_enabled_course_tools( request, course_key) dates_widget = { 'course_date_blocks': [], 'dates_tab_link': dates_tab_link, 'user_timezone': user_timezone, } enroll_alert = { 'can_enroll': True, 'extra_text': None, } handouts_html = None offer_data = None resume_course = { 'has_visited_course': False, 'url': None, } welcome_message_html = None is_enrolled = enrollment and enrollment.is_active is_staff = bool(has_access(request.user, 'staff', course_key)) show_enrolled = is_enrolled or is_staff if show_enrolled: course_blocks = get_course_outline_block_tree( request, course_key_string, request.user) date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=1) dates_widget['course_date_blocks'] = [ block for block in date_blocks if not isinstance(block, TodaysDate) ] handouts_html = get_course_info_section(request, request.user, course, 'handouts') welcome_message_html = get_current_update_for_user(request, course) offer_data = generate_offer_data(request.user, course_overview) access_expiration = get_access_expiration_data( request.user, course_overview) # Only show the set course goal message for enrolled, unverified # users in a course that allows for verified statuses. is_already_verified = CourseEnrollment.is_enrolled_as_verified( request.user, course_key) if not is_already_verified and has_course_goal_permission( request, course_key_string, {'is_enrolled': is_enrolled}): course_goals = { 'goal_options': valid_course_goals_ordered(include_unsure=True), 'selected_goal': None } selected_goal = get_course_goal(request.user, course_key) if selected_goal: course_goals['selected_goal'] = { 'key': selected_goal.goal_key, 'text': get_course_goal_text(selected_goal.goal_key), } try: resume_block = get_key_to_last_completed_block( request.user, course.id) resume_course['has_visited_course'] = True resume_path = reverse('jump_to', kwargs={ 'course_id': course_key_string, 'location': str(resume_block) }) resume_course['url'] = request.build_absolute_uri(resume_path) except UnavailableCompletionData: start_block = get_start_block(course_blocks) resume_course['url'] = start_block['lms_web_url'] elif allow_public_outline or allow_public or user_is_masquerading: course_blocks = get_course_outline_block_tree( request, course_key_string, None) if allow_public or user_is_masquerading: handouts_html = get_course_info_section( request, request.user, course, 'handouts') 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 data = { 'access_expiration': access_expiration, 'course_blocks': course_blocks, 'course_goals': course_goals, 'course_tools': course_tools, 'dates_widget': dates_widget, 'enroll_alert': enroll_alert, 'handouts_html': handouts_html, 'has_ended': course.has_ended(), 'offer': offer_data, 'resume_course': resume_course, 'welcome_message_html': welcome_message_html, } context = self.get_serializer_context() context['course_overview'] = course_overview context['enable_links'] = show_enrolled or allow_public context['enrollment'] = enrollment serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def test_enabled_block_types_with_assignments(self): # pylint: disable=too-many-statements """ Creates a course with multiple subsections to test all of the different cases for assignment dates showing up. Mocks out calling the edx-when service and then validates the correct data is set and returned. """ course = create_course_run(days_till_start=-100) user = create_user() request = RequestFactory().request() request.user = user CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED) now = datetime.now(utc) assignment_title_html = ['<a href=', '</a>'] with self.store.bulk_operations(course.id): section = ItemFactory.create(category='chapter', parent_location=course.location) subsection_1 = ItemFactory.create( category='sequential', display_name='Released', parent_location=section.location, start=now - timedelta(days=1), due=now + timedelta(days=6), graded=True, ) subsection_2 = ItemFactory.create( category='sequential', display_name='Not released', parent_location=section.location, start=now + timedelta(days=1), due=now + timedelta(days=7), graded=True, ) subsection_3 = ItemFactory.create( category='sequential', display_name='Third nearest assignment', parent_location=section.location, start=now + timedelta(days=1), due=now + timedelta(days=8), graded=True, ) subsection_4 = ItemFactory.create( category='sequential', display_name='Past due date', parent_location=section.location, start=now - timedelta(days=14), due=now - timedelta(days=7), graded=True, ) subsection_5 = ItemFactory.create( category='sequential', display_name='Not returned since we do not get non-graded subsections', parent_location=section.location, start=now + timedelta(days=1), due=now - timedelta(days=7), graded=False, ) subsection_6 = ItemFactory.create( category='sequential', display_name='No start date', parent_location=section.location, start=None, due=now + timedelta(days=9), graded=True, ) subsection_7 = ItemFactory.create( category='sequential', # Setting display name to None should set the assignment title to 'Assignment' display_name=None, parent_location=section.location, start=now - timedelta(days=14), due=now + timedelta(days=10), graded=True, ) dummy_subsection = ItemFactory.create(category='sequential') with patch('lms.djangoapps.courseware.courses.get_dates_for_course') as mock_get_dates: mock_get_dates.return_value = { (subsection_1.location, 'due'): subsection_1.due, (subsection_1.location, 'start'): subsection_1.start, (subsection_2.location, 'due'): subsection_2.due, (subsection_2.location, 'start'): subsection_2.start, (subsection_3.location, 'due'): subsection_3.due, (subsection_3.location, 'start'): subsection_3.start, (subsection_4.location, 'due'): subsection_4.due, (subsection_4.location, 'start'): subsection_4.start, (subsection_5.location, 'due'): subsection_5.due, (subsection_5.location, 'start'): subsection_5.start, (subsection_6.location, 'due'): subsection_6.due, (subsection_7.location, 'due'): subsection_7.due, (subsection_7.location, 'start'): subsection_7.start, # Adding this in for the case where we return a block that # doesn't actually exist as part of the course. Should just be ignored. (dummy_subsection.location, 'due'): dummy_subsection.due, } # Standard widget case where we restrict the number of assignments. expected_blocks = ( TodaysDate, CourseAssignmentDate, CourseAssignmentDate, CourseEndDate, VerificationDeadlineDate ) blocks = get_course_date_blocks(course, user, request, num_assignments=2) self.assertEqual(len(blocks), len(expected_blocks)) self.assertEqual(set(type(b) for b in blocks), set(expected_blocks)) assignment_blocks = filter(lambda b: isinstance(b, CourseAssignmentDate), blocks) for assignment in assignment_blocks: assignment_title = str(assignment.title_html) or str(assignment.title) self.assertNotEqual(assignment_title, 'Third nearest assignment') self.assertNotEqual(assignment_title, 'Past due date') self.assertNotEqual(assignment_title, 'Not returned since we do not get non-graded subsections') # checking if it is _in_ the title instead of being the title since released assignments # are actually links. Unreleased assignments are just the string of the title. if 'Released' in assignment_title: for html_tag in assignment_title_html: self.assertIn(html_tag, assignment_title) elif assignment_title == 'Not released': for html_tag in assignment_title_html: self.assertNotIn(html_tag, assignment_title) # No restrictions on number of assignments to return expected_blocks = ( CourseStartDate, TodaysDate, CourseAssignmentDate, CourseAssignmentDate, CourseAssignmentDate, CourseAssignmentDate, CourseAssignmentDate, CourseAssignmentDate, CourseEndDate, VerificationDeadlineDate ) blocks = get_course_date_blocks(course, user, request, include_past_dates=True) self.assertEqual(len(blocks), len(expected_blocks)) self.assertEqual(set(type(b) for b in blocks), set(expected_blocks)) assignment_blocks = filter(lambda b: isinstance(b, CourseAssignmentDate), blocks) for assignment in assignment_blocks: assignment_title = str(assignment.title_html) or str(assignment.title) self.assertNotEqual(assignment_title, 'Not returned since we do not get non-graded subsections') # checking if it is _in_ the title instead of being the title since released assignments # are actually links. Unreleased assignments are just the string of the title. if 'Released' in assignment_title: for html_tag in assignment_title_html: self.assertIn(html_tag, assignment_title) elif assignment_title == 'Not released': for html_tag in assignment_title_html: self.assertNotIn(html_tag, assignment_title) elif assignment_title == 'Third nearest assignment': # It's still not released for html_tag in assignment_title_html: self.assertNotIn(html_tag, assignment_title) elif 'Past due date' in assignment_title: self.assertGreater(now, assignment.date) for html_tag in assignment_title_html: self.assertIn(html_tag, assignment_title) elif 'No start date' == assignment_title: # Can't determine if it is released so it does not get a link for html_tag in assignment_title_html: self.assertNotIn(html_tag, assignment_title) # This is the item with no display name where we set one ourselves. elif 'Assignment' in assignment_title: # Can't determine if it is released so it does not get a link for html_tag in assignment_title_html: self.assertIn(html_tag, assignment_title)