示例#1
0
 def content_type_gating_enabled(self):
     course_key = self.course_key
     user = self.effective_user
     is_enabled = None
     course_masquerade = self.course_masquerade
     if is_masquerading(user, course_key, course_masquerade):
         if is_masquerading_as_staff(user, course_key):
             is_enabled = False
         elif is_masquerading_as_full_access(user, course_key,
                                             course_masquerade):
             is_enabled = False
         elif is_masquerading_as_non_audit_enrollment(
                 user, course_key, course_masquerade):
             is_enabled = False
         elif is_masquerading_as_audit_enrollment(user, course_key,
                                                  course_masquerade):
             is_enabled = ContentTypeGatingConfig.enabled_for_course(
                 course_key)
         elif is_masquerading_as_limited_access(user, course_key,
                                                course_masquerade):
             is_enabled = ContentTypeGatingConfig.enabled_for_course(
                 course_key)
     if is_enabled is None:
         is_enabled = ContentTypeGatingConfig.enabled_for_enrollment(
             user=user,
             course_key=course_key,
         )
     return is_enabled
示例#2
0
    def record_user_activity(cls, user, course_key, request=None, only_if_mobile_app=False):
        '''
        Update the user activity table with a record for this activity.

        Since we store one activity per date, we don't need to query the database
        for every activity on a given date.
        To avoid unnecessary queries, we store a record in a cache once we have an activity for the date,
        which times out at the end of that date (in the user's timezone).

        The request argument is only used to check if the request is coming from a mobile app.
        Once the only_if_mobile_app argument is removed the request argument can be removed as well.

        The return value is the id of the object that was created, or retrieved.
        A return value of None signifies that there was an issue with the parameters (or the user was masquerading).
        '''
        if not (user and user.id) or not course_key:
            return None

        if only_if_mobile_app and request and not is_request_from_mobile_app(request):
            return None

        if is_masquerading(user, course_key):
            return None

        user_preferences = get_user_preferences(user)
        timezone = pytz.timezone(user_preferences.get('time_zone', 'UTC'))
        now = datetime.now(timezone)
        date = now.date()

        cache_key = 'goals_user_activity_{}_{}_{}'.format(str(user.id), str(course_key), str(date))

        cached_value = TieredCache.get_cached_response(cache_key)
        if cached_value.is_found:
            # Temporary debugging log for testing mobile app connection
            if request:
                log.info(
                    'Retrieved cached value with request {} for user and course combination {} {}'.format(
                        str(request.build_absolute_uri()), str(user.id), str(course_key)
                    )
                )
            return cached_value.value, False

        activity_object, __ = cls.objects.get_or_create(user=user, course_key=course_key, date=date)

        # Cache result until the end of the day to avoid unnecessary database requests
        tomorrow = now + timedelta(days=1)
        midnight = datetime(year=tomorrow.year, month=tomorrow.month,
                            day=tomorrow.day, hour=0, minute=0, second=0, tzinfo=timezone)
        seconds_until_midnight = (midnight - now).seconds

        TieredCache.set_all_tiers(cache_key, activity_object.id, seconds_until_midnight)
        # Temporary debugging log for testing mobile app connection
        if request:
            log.info(
                'Set cached value with request {} for user and course combination {} {}'.format(
                    str(request.build_absolute_uri()), str(user.id), str(course_key)
                )
            )
        return activity_object.id
    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)
示例#4
0
def reset_course_deadlines(request):
    """
    Set the start_date of a schedule to today, which in turn will adjust due dates for
    sequentials belonging to a self paced course

    Request Parameters:
        course_key: course key
        research_event_data: any data that should be included in the research tracking event
            Example: sending the location of where the reset deadlines banner (i.e. outline-tab)

    IMPORTANT NOTE: If updates are happening to the logic here, ALSO UPDATE the `reset_course_deadlines`
    function in common/djangoapps/util/views.py as well.
    """
    course_key = request.data.get('course_key', None)
    research_event_data = request.data.get('research_event_data', {})

    # If body doesnt contain 'course_key', return 400 to client.
    if not course_key:
        raise ParseError(_("'course_key' is required."))

    try:
        course_key = CourseKey.from_string(course_key)
        course_masquerade, user = setup_masquerade(
            request, course_key, has_access(request.user, 'staff', course_key))

        # We ignore the missed_deadlines because this endpoint is used in the Learning MFE for
        # learners who have remaining attempts on a problem and reset their due dates in order to
        # submit additional attempts. This can apply for 'completed' (submitted) content that would
        # not be marked as past_due
        _missed_deadlines, missed_gated_content = dates_banner_should_display(
            course_key, user)
        if not missed_gated_content:
            reset_self_paced_schedule(user, course_key)

            course_overview = course_detail(request, user.username, course_key)
            # For context here, research_event_data should already contain `location` indicating
            # the page/location dates were reset from and could also contain `block_id` if reset
            # within courseware.
            research_event_data.update({
                'courserun_key':
                str(course_key),
                'is_masquerading':
                is_masquerading(user, course_key, course_masquerade),
                'is_staff':
                has_access(user, 'staff', course_key).has_access,
                'org_key':
                course_overview.display_org_with_default,
                'user_id':
                user.id,
            })
            tracker.emit('edx.ui.lms.reset_deadlines.clicked',
                         research_event_data)

        if course_home_legacy_is_active(course_key):
            body_link = '{}{}'.format(settings.LMS_ROOT_URL,
                                      reverse('dates', args=[str(course_key)]))
        else:
            body_link = get_learning_mfe_home_url(course_key=str(course_key),
                                                  view_name='dates')

        return Response({
            'body':
            format_html('<a href="{}">{}</a>', body_link, _('View all dates')),
            'header':
            _('Your due dates have been successfully shifted to help you stay on track.'
              ),
            'link':
            body_link,
            'link_text':
            _('View all dates'),
            'message':
            _('Deadlines successfully reset.'),
        })
    except Exception as reset_deadlines_exception:
        log.exception('Error occurred while trying to reset deadlines!')
        raise UnableToResetDeadlines from reset_deadlines_exception
示例#5
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 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)