def render_to_fragment(self, request, course_id=None, **kwargs): """ Renders the welcome message fragment for the specified course. Returns: A fragment, or None if there is no welcome message. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) welcome_message_html = self.welcome_message_html(request, course) if not welcome_message_html: return None dismiss_url = reverse( 'openedx.course_experience.dismiss_welcome_message', kwargs={'course_id': unicode(course_key)} ) context = { 'dismiss_url': dismiss_url, 'welcome_message_html': welcome_message_html, } if get_course_tag(request.user, course_key, PREFERENCE_KEY) == 'False': return None else: html = render_to_string('course_experience/welcome-message-fragment.html', context) return Fragment(html)
def render_to_fragment(self, request, course_id=None, **kwargs): """ Renders the welcome message fragment for the specified course. Returns: A fragment, or None if there is no welcome message. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) welcome_message_html = self.welcome_message_html(request, course) if not welcome_message_html: return None dismiss_url = reverse( 'openedx.course_experience.dismiss_welcome_message', kwargs={'course_id': six.text_type(course_key)}) context = { 'dismiss_url': dismiss_url, 'welcome_message_html': welcome_message_html, } if get_course_tag(request.user, course_key, PREFERENCE_KEY) == 'False': return None else: html = render_to_string( 'course_experience/welcome-message-fragment.html', context) return Fragment(html)
def test_get_set_course_tag(self): # get a tag that doesn't exist tag = course_tag_api.get_course_tag(self.user, self.course_id, self.test_key) assert tag is None # test setting a new key test_value = 'value' course_tag_api.set_course_tag(self.user, self.course_id, self.test_key, test_value) tag = course_tag_api.get_course_tag(self.user, self.course_id, self.test_key) assert tag == test_value #test overwriting an existing key test_value = 'value2' course_tag_api.set_course_tag(self.user, self.course_id, self.test_key, test_value) tag = course_tag_api.get_course_tag(self.user, self.course_id, self.test_key) assert tag == test_value
def test_get_set_course_tag(self): # get a tag that doesn't exist tag = course_tag_api.get_course_tag(self.user, self.course_id, self.test_key) self.assertIsNone(tag) # test setting a new key test_value = 'value' course_tag_api.set_course_tag(self.user, self.course_id, self.test_key, test_value) tag = course_tag_api.get_course_tag(self.user, self.course_id, self.test_key) self.assertEqual(tag, test_value) #test overwriting an existing key test_value = 'value2' course_tag_api.set_course_tag(self.user, self.course_id, self.test_key, test_value) tag = course_tag_api.get_course_tag(self.user, self.course_id, self.test_key) self.assertEqual(tag, test_value)
def get_group_for_user(cls, course_key, user, user_partition, assign=True): """ Returns the group from the specified user position to which the user is assigned. If the user has not yet been assigned, a group will be randomly chosen for them if assign flag is True. """ partition_key = cls.key_for_partition(user_partition) group_id = course_tag_api.get_course_tag(user, course_key, partition_key) group = None if group_id is not None: # attempt to look up the presently assigned group try: group = user_partition.get_group(int(group_id)) except NoSuchUserPartitionGroupError: # jsa: we can turn off warnings here if this is an expected case. log.warn(u"group not found in RandomUserPartitionScheme: %r", { "requested_partition_id": user_partition.id, "requested_group_id": group_id, }, exc_info=True) except ValueError: log.error(u"Bad group_id %r for user: %r", group_id, user) if group is None and assign and not course_tag_api.BulkCourseTags.is_prefetched( course_key): if not user_partition.groups: raise UserPartitionError( 'Cannot assign user to an empty user partition') # pylint: disable=fixme # TODO: had a discussion in arch council about making randomization more # deterministic (e.g. some hash). Could do that, but need to be careful not # to introduce correlation between users or bias in generation. group = cls.RANDOM.choice(user_partition.groups) # persist the value as a course tag course_tag_api.set_course_tag(user, course_key, partition_key, group.id) # emit event for analytics # FYI - context is always user ID that is logged in, NOT the user id that is # being operated on. If instructor can move user explicitly, then we should # put in event_info the user id that is being operated on. event_name = 'xmodule.partitions.assigned_user_to_partition' event_info = { 'group_id': group.id, 'group_name': group.name, 'partition_id': user_partition.id, 'partition_name': user_partition.name } # pylint: disable=fixme # TODO: Use the XBlock publish api instead with tracker.get_tracker().context(event_name, {}): tracker.emit( event_name, event_info, ) return group
def _get_dismissed_hashes(user, course_key): """ Returns a list of dismissed hashes, or None if all updates have been dismissed. """ view_welcome_message = get_course_tag(user, course_key, VIEW_WELCOME_MESSAGE_KEY) if view_welcome_message == 'False': # legacy value, which dismisses all updates return None return view_welcome_message.split(',') if view_welcome_message else []
def test_dismissal_hashing(self): """Confirm that the stored dismissal values are what we expect, to catch accidentally changing formats.""" self.create_course_update('First') dismiss_current_update_for_user(self.request, self.course) self.create_course_update('Second') dismiss_current_update_for_user(self.request, self.course) tag = get_course_tag(self.user, self.course.id, self.UPDATES_TAG) assert tag == '7fb55ed0b7a30342ba6da306428cae04,c22cf8376b1893dcfcef0649fe1a7d87'
def get_tag(self, scope, key): """ Get a user tag for the current course and the current user for a given key scope: the current scope of the runtime key: the key for the value we want """ if scope != user_course_tag_api.COURSE_SCOPE: raise ValueError("unexpected scope {0}".format(scope)) return user_course_tag_api.get_course_tag(self._get_current_user(), self.runtime.course_id, key)
def get_tag(self, scope, key): """ Get a user tag for the current course and the current user for a given key scope: the current scope of the runtime key: the key for the value we want """ if scope != user_course_tag_api.COURSE_SCOPE: raise ValueError("unexpected scope {0}".format(scope)) return user_course_tag_api.get_course_tag( self._get_current_user(), self.runtime.course_id, key )
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) 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_get_course_tag_for_anonymous_user(self): tag = course_tag_api.get_course_tag(AnonymousUser(), self.course_id, self.test_key) assert tag is None
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 render_to_fragment(self, request, course_id=None, **kwargs): """ Renders the course's home page as a fragment. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key) PREFERENCE_KEY = 'view-welcome-message' # Render the course dates as a fragment dates_fragment = CourseDatesFragmentView().render_to_fragment( request, course_id=course_id, **kwargs) # Render the full content to enrolled users, as well as to course and global staff. # Unenrolled users who are not course or global staff are given only a subset. enrollment = CourseEnrollment.get_enrollment(request.user, course_key) user_access = { 'is_anonymous': request.user.is_anonymous(), 'is_enrolled': enrollment is not None, 'is_staff': has_access(request.user, 'staff', course_key), } if user_access['is_enrolled'] or user_access['is_staff']: outline_fragment = CourseOutlineFragmentView().render_to_fragment( request, course_id=course_id, **kwargs) if (get_course_tag(request.user, course_key, PREFERENCE_KEY) == 'False' or LATEST_UPDATE_FLAG.is_enabled(course_key)): update_message_fragment = UpdatesFragmentView( ).render_to_fragment(request, course_id=course_id, **kwargs) else: update_message_fragment = WelcomeMessageFragmentView( ).render_to_fragment(request, course_id=course_id, **kwargs) course_sock_fragment = CourseSockFragmentView().render_to_fragment( request, course=course, **kwargs) has_visited_course, resume_course_url = self._get_resume_course_info( request, course_id) else: # Redirect the user to the dashboard if they are not enrolled and # this is a course that does not support direct enrollment. if not can_self_enroll_in_course(course_key): raise CourseAccessRedirect(reverse('dashboard')) # Set all the fragments outline_fragment = None update_message_fragment = None course_sock_fragment = None has_visited_course = None resume_course_url = None # Get the handouts handouts_html = self._get_course_handouts(request, course) # Get the course tools enabled for this user and course course_tools = CourseToolsPluginManager.get_enabled_course_tools( request, course_key) # Check if the user can access the course goal functionality has_goal_permission = has_course_goal_permission( request, course_id, user_access) # Grab the current course goal and the acceptable course goal keys mapped to translated values current_goal = get_course_goal(request.user, course_key) goal_options = get_course_goal_options() # Get the course goals api endpoint goal_api_url = get_goal_api_url(request) # Grab the course home messages fragment to render any relevant django messages course_home_message_fragment = CourseHomeMessageFragmentView( ).render_to_fragment(request, course_id=course_id, user_access=user_access, **kwargs) # Get info for upgrade messaging upgrade_price = None upgrade_url = None # TODO Add switch to control deployment if SHOW_UPGRADE_MSG_ON_COURSE_HOME.is_enabled( course_key) and enrollment and enrollment.upgrade_deadline: upgrade_url = EcommerceService().upgrade_url( request.user, course_key) upgrade_price = get_cosmetic_verified_display_price(course) if not request.user.is_anonymous(): course_optouts = Optout.objects.filter( user=request.user).values_list('course_id', flat=True) else: course_optouts = None # Render the course home fragment context = { 'request': request, 'csrf': csrf(request)['csrf_token'], 'course': course, 'course_key': course_key, 'outline_fragment': outline_fragment, 'handouts_html': handouts_html, 'course_home_message_fragment': course_home_message_fragment, 'has_visited_course': has_visited_course, 'resume_course_url': resume_course_url, 'course_tools': course_tools, 'dates_fragment': dates_fragment, 'username': request.user.username, 'goal_api_url': goal_api_url, 'has_goal_permission': has_goal_permission, 'goal_options': goal_options, 'current_goal': current_goal, 'update_message_fragment': update_message_fragment, 'course_sock_fragment': course_sock_fragment, 'disable_courseware_js': True, 'uses_pattern_library': True, 'upgrade_price': upgrade_price, 'upgrade_url': upgrade_url, 'course_optouts': course_optouts, } html = render_to_string('course_experience/course-home-fragment.html', context) return Fragment(html)