Beispiel #1
0
 def get_dates_banner_info(self, _):
     """
     Serializer mixin for returning date banner info.  Gets its input from
     the views course_key_string url parameter and the request's user object.
     """
     info = {
         'missed_deadlines': False,
         'content_type_gating_enabled': False,
     }
     course_key_string = self.context['view'].kwargs.get(
         'course_key_string')
     if course_key_string:
         course_key = CourseKey.from_string(course_key_string)
         request = self.context['request']
         missed_deadlines, missed_gated_content = dates_banner_should_display(
             course_key, request.user)
         info['missed_deadlines'] = missed_deadlines
         info['missed_gated_content'] = missed_gated_content
         info[
             'content_type_gating_enabled'] = ContentTypeGatingConfig.enabled_for_enrollment(
                 user=request.user,
                 course_key=course_key,
             )
         info['verified_upgrade_link'] = verified_upgrade_deadline_link(
             request.user, course_id=course_key)
     return info
Beispiel #2
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
    """
    from lms.urls import RENDER_XBLOCK_NAME
    from openedx.features.course_experience.urls import COURSE_HOME_VIEW_NAME

    detail_id_dict = ast.literal_eval(
        request.POST.get('reset_deadlines_redirect_url_id_dict'))
    redirect_url = request.POST.get('reset_deadlines_redirect_url_base',
                                    COURSE_HOME_VIEW_NAME)
    course_key = CourseKey.from_string(detail_id_dict['course_id'])
    masquerade_details, masquerade_user = setup_masquerade(
        request, course_key, has_access(request.user, 'staff', course_key))
    if masquerade_details and masquerade_details.role == 'student' and masquerade_details.user_name and (
            redirect_url == COURSE_HOME_VIEW_NAME):
        # Masquerading as a specific student, so reset that student's schedule
        user = masquerade_user
    else:
        user = request.user

    missed_deadlines, missed_gated_content = dates_banner_should_display(
        course_key, user)
    if missed_deadlines and not missed_gated_content:
        reset_self_paced_schedule(user, course_key)
    if redirect_url == RENDER_XBLOCK_NAME:
        detail_id_dict.pop('course_id')
    return redirect(reverse(redirect_url, kwargs=detail_id_dict))
Beispiel #3
0
    def get_ctas(self, xblock, category, completed):
        """
        Return the calls to action associated with the specified category for the given xblock.

        Look at CallToActionService docstring to see what will be returned.
        """
        ctas = []
        request = get_current_request()

        course_key = xblock.scope_ids.usage_id.context_key
        missed_deadlines, missed_gated_content = dates_banner_should_display(course_key, request.user)
        # Not showing in the missed_gated_content case because those learners are not eligible
        # to shift due dates.
        if not missed_deadlines or missed_gated_content:
            return []

        # Some checks to disable PLS calls to action until these environments (mobile and MFE) support them natively
        if request and is_request_from_mobile_app(request):
            return []

        is_learning_mfe = request and is_request_from_learning_mfe(request)
        if category == self.CAPA_SUBMIT_DISABLED:
            # xblock is a capa problem, and the submit button is disabled. Check if it's because of a personalized
            # schedule due date being missed, and if so, we can offer to shift it.
            if self._is_block_shiftable(xblock, category):
                ctas.append(self._make_reset_deadlines_cta(xblock, category, is_learning_mfe))

        elif category == self.VERTICAL_BANNER and not completed:
            # xblock is a vertical, so we'll check all the problems inside it. If there are any that will show a
            # a "shift dates" CTA under CAPA_SUBMIT_DISABLED, then we'll also show the same CTA as a vertical banner.
            if any(self._is_block_shiftable(item, category) for item in xblock.get_display_items()):
                ctas.append(self._make_reset_deadlines_cta(xblock, category, is_learning_mfe))

        return ctas
Beispiel #4
0
    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, user_is_enrolled=True, **kwargs):  # pylint: disable=arguments-differ
        """
        Renders the course outline as a fragment.
        """
        from lms.urls import RESET_COURSE_DEADLINES_NAME

        course_key = CourseKey.from_string(course_id)
        course_overview = get_course_overview_with_access(
            request.user, 'load', course_key, check_if_enrolled=user_is_enrolled
        )
        course = modulestore().get_course(course_key)

        course_block_tree = get_course_outline_block_tree(
            request, course_id, request.user if user_is_enrolled else None
        )
        if not course_block_tree:
            return None

        resume_block = get_resume_block(course_block_tree) if user_is_enrolled else None

        if not resume_block:
            self.mark_first_unit_to_resume(course_block_tree)

        xblock_display_names = self.create_xblock_id_and_name_dict(course_block_tree)
        gated_content = self.get_content_milestones(request, course_key)

        missed_deadlines, missed_gated_content = dates_banner_should_display(course_key, request.user)

        reset_deadlines_url = reverse(RESET_COURSE_DEADLINES_NAME)

        context = {
            'csrf': csrf(request)['csrf_token'],
            'course': course_overview,
            'due_date_display_format': course.due_date_display_format,
            'blocks': course_block_tree,
            'enable_links': user_is_enrolled or course.course_visibility == COURSE_VISIBILITY_PUBLIC,
            'course_key': course_key,
            'gated_content': gated_content,
            'xblock_display_names': xblock_display_names,
            'self_paced': course.self_paced,

            # We're using this flag to prevent old self-paced dates from leaking out on courses not
            # managed by edx-when.
            'in_edx_when': edx_when_api.is_enabled_for_course(course_key),
            'reset_deadlines_url': reset_deadlines_url,
            'verified_upgrade_link': verified_upgrade_deadline_link(request.user, course=course),
            'on_course_outline_page': True,
            'missed_deadlines': missed_deadlines,
            'missed_gated_content': missed_gated_content,
            'has_ended': course.has_ended(),
        }

        html = render_to_string('course_experience/course-outline-fragment.html', context)
        return Fragment(html)
Beispiel #6
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

    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)

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

    # If body contains params other than 'course_key', return 400 to client.
    if len(request.data) > 1:
        raise ParseError(_("Only 'course_key' is expected."))

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

        missed_deadlines, missed_gated_content = dates_banner_should_display(
            course_key, user)
        if missed_deadlines and not missed_gated_content:
            reset_self_paced_schedule(user, course_key)

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

        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 e:
        log.exception(e)
        raise UnableToResetDeadlines
Beispiel #7
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
    """
    course_key = CourseKey.from_string(request.POST.get('course_id'))
    _course_masquerade, user = setup_masquerade(
        request, course_key, has_access(request.user, 'staff', course_key))

    missed_deadlines, missed_gated_content = dates_banner_should_display(
        course_key, user)
    if missed_deadlines and not missed_gated_content:
        reset_self_paced_schedule(user, course_key)

    referrer = request.META.get('HTTP_REFERER')
    return redirect(referrer) if referrer else HttpResponse()
Beispiel #8
0
    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)
Beispiel #9
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

    IMPORTANT NOTE: If updates are happening to the logic here, ALSO UPDATE the `reset_course_deadlines`
    function in openedx/features/course_experience/api/v1/views.py as well.
    """
    course_key = CourseKey.from_string(request.POST.get('course_id'))
    _course_masquerade, user = setup_masquerade(
        request, course_key, has_access(request.user, 'staff', course_key))

    missed_deadlines, missed_gated_content = dates_banner_should_display(
        course_key, user)
    if missed_deadlines and not missed_gated_content:
        reset_self_paced_schedule(user, course_key)

    referrer = request.META.get('HTTP_REFERER')
    return redirect(referrer) if referrer else HttpResponse()
Beispiel #10
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

    IMPORTANT NOTE: If updates are happening to the logic here, ALSO UPDATE the `reset_course_deadlines`
    function in openedx/features/course_experience/api/v1/views.py as well.
    """
    course_key = CourseKey.from_string(request.POST.get('course_id'))
    _course_masquerade, user = setup_masquerade(
        request, course_key, has_access(request.user, 'staff', course_key))

    # We ignore the missed_deadlines because this endpoint could be used 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)

    referrer = request.META.get('HTTP_REFERER')
    return redirect(referrer) if referrer else HttpResponse()
Beispiel #11
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