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)
Exemple #3
0
    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)
Exemple #5
0
    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
Exemple #6
0
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'
Exemple #8
0
    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)
Exemple #9
0
    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
        )
Exemple #10
0
    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)
Exemple #11
0
    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)
Exemple #12
0
 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
Exemple #13
0
    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)
Exemple #14
0
    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)