예제 #1
0
 def test_streak_resets_if_day_is_missed(self):
     """
     Sample run for a 3 day streak and 1 day break with the learner coming back every other day.
     Therefore the streak keeps resetting.
     +---------+---------------------+--------------------+-------------------------+------------------+-----------------------------------------------+
     | today   | streak_length       | last_day_of_streak | streak_length_to_celebrate | Note                                          |
     +---------+---------------------+--------------------+-------------------------+------------------+-----------------------------------------------+
     | 2/4/21  | 1                   | 2/4/21             | None                    | Day 1 of Streak                               |
       No Accesses on 2/5/21
     | 2/6/21  | 1                   | 2/6/21             | None                    | Day 2 of streak was missed, so streak resets  |
       No Accesses on 2/7/21
     | 2/8/21  | 1                   | 2/8/21             | None                    | Day 2 of streak was missed, so streak resets  |
       No Accesses on 2/9/21
     | 2/10/21 | 1                   | 2/10/21            | None                    | Day 2 of streak was missed, so streak resets  |
       No Accesses on 2/11/21
     | 2/12/21 | 1                   | 2/12/21            | None                    | Day 2 of streak was missed, so streak resets  |
     +---------+---------------------+--------------------+-------------------------+------------------+-----------------------------------------------+
     """
     now = datetime.datetime.now(UTC)
     for i in range(1, self.STREAK_LENGTH_TO_CELEBRATE * 3 + 1, 2):
         with freeze_time(now + datetime.timedelta(days=i)):
             streak_length_to_celebrate = UserCelebration.perform_streak_updates(
                 self.user, self.course_key)
             assert self.user.celebration.last_day_of_streak == (
                 now + datetime.timedelta(days=i)).date()
             assert streak_length_to_celebrate is None
예제 #2
0
 def test_streak_does_not_reset_if_day_is_missed_with_longer_break(self):
     """
     Sample run for a 3 day streak with the learner coming back every other day.
     See last column for explanation.
     +---------+---------------------+--------------------+-------------------------+------------------+
     | today   | streak_length       | last_day_of_streak | streak_length_to_celebrate | Note          |
     +---------+---------------------+--------------------+-------------------------+------------------+
     | 2/4/21  | 1                   | 2/4/21             | None                    | Day 1 of Streak  |
       No Accesses on 2/5/21
     | 2/6/21  | 2                   | 2/6/21             | None                    | Day 2 of Streak  |
       No Accesses on 2/7/21
     | 2/8/21  | 3                   | 2/8/21             | 3                       | Day 3 of streak  |
       No Accesses on 2/9/21
     | 2/10/21 | 4                   | 2/10/21            | None                    | Day 4 of streak  |
       No Accesses on 2/11/21
     | 2/12/21 | 5                   | 2/12/21            | None                    | Day 5 of streak  |
     +---------+---------------------+--------------------+-------------------------+------------------+
     """
     UserCelebration.STREAK_BREAK_LENGTH = 2
     now = datetime.datetime.now(UTC)
     for i in range(1, self.STREAK_LENGTH_TO_CELEBRATE * 3 + 1, 2):
         with freeze_time(now + datetime.timedelta(days=i)):
             streak_length_to_celebrate = UserCelebration.perform_streak_updates(
                 self.user, self.course_key)
             assert bool(streak_length_to_celebrate) == (i == 5)
예제 #3
0
 def test_first_check_streak_celebration(self):
     STREAK_LENGTH_TO_CELEBRATE = UserCelebration.perform_streak_updates(
         self.user, self.course_key)
     today = datetime.datetime.now(UTC).date()
     assert self.user.celebration.streak_length == 1
     assert self.user.celebration.last_day_of_streak == today
     assert STREAK_LENGTH_TO_CELEBRATE is None
예제 #4
0
 def test_celebrate_twice_with_broken_streak_in_between(self):
     """
     Sample run for a 3 day streak and 1 day break. See last column for explanation.
     +---------+---------------------+--------------------+-------------------------+------------------+-----------------------------------------------+
     | today   | streak_length       | last_day_of_streak | streak_length_to_celebrate | Note                                |
     +---------+---------------------+--------------------+-------------------------+------------------+-----------------------------------------------+
     | 2/4/21  | 1                   | 2/4/21             | None                    | Day 1 of Streak                               |
     | 2/5/21  | 2                   | 2/5/21             | None                    | Day 2 of Streak                               |
     | 2/6/21  | 3                   | 2/6/21             | 3                       | Completed 3 Day Streak so we should celebrate |
       No Accesses on 2/7/21
     | 2/8/21  | 1                   | 2/8/21             | None                    | Day 1 of Streak                               |
     | 2/9/21  | 2                   | 2/9/21             | None                    | Day 2 of Streak                               |
     | 2/10/21 | 3                   | 2/10/21            | 3                       | Completed 3 Day Streak so we should celebrate |
     +---------+---------------------+--------------------+-------------------------+------------------+-----------------------------------------------+
     """
     now = datetime.datetime.now(UTC)
     for i in range(
             1, self.STREAK_LENGTH_TO_CELEBRATE + self.STREAK_BREAK_LENGTH +
             self.STREAK_LENGTH_TO_CELEBRATE + 1):
         with freeze_time(now + datetime.timedelta(days=i)):
             if self.STREAK_LENGTH_TO_CELEBRATE < i <= self.STREAK_LENGTH_TO_CELEBRATE + self.STREAK_BREAK_LENGTH:
                 # Don't make any checks during the break
                 continue
             streak_length_to_celebrate = UserCelebration.perform_streak_updates(
                 self.user, self.course_key)
             if i <= self.STREAK_LENGTH_TO_CELEBRATE:
                 assert bool(streak_length_to_celebrate) == (
                     i == self.STREAK_LENGTH_TO_CELEBRATE)
             else:
                 assert bool(streak_length_to_celebrate) == (
                     i == self.STREAK_LENGTH_TO_CELEBRATE +
                     self.STREAK_BREAK_LENGTH +
                     self.STREAK_LENGTH_TO_CELEBRATE)
예제 #5
0
def get_celebrations_dict(user, enrollment, course, browser_timezone):
    """
    Returns a dict of celebrations that should be performed.
    """
    if not enrollment:
        return {
            'first_section': False,
            'streak_length_to_celebrate': None,
            'streak_discount_enabled': False,
        }

    streak_length_to_celebrate = UserCelebration.perform_streak_updates(
        user, course.id, browser_timezone
    )
    celebrations = {
        'first_section': CourseEnrollmentCelebration.should_celebrate_first_section(enrollment),
        'streak_length_to_celebrate': streak_length_to_celebrate,
        'streak_discount_enabled': False,
    }

    if streak_length_to_celebrate:
        # We only want to offer the streak discount
        # if the course has not ended, is upgradeable and the user is not an enterprise learner

        if can_show_streak_discount_coupon(user, course):
            # Send course streak coupon event
            course_key = str(course.id)
            modes_dict = CourseMode.modes_for_course_dict(course_id=course_key, include_expired=False)
            verified_mode = modes_dict.get('verified', None)
            if verified_mode:
                celebrations['streak_discount_enabled'] = True

    return celebrations
예제 #6
0
def get_celebrations_dict(user, enrollment, course, browser_timezone):
    """
    Returns a dict of celebrations that should be performed.
    """
    if not enrollment:
        return {
            'first_section': False,
            'streak_length_to_celebrate': None,
            'streak_discount_experiment_enabled': False,
        }

    streak_length_to_celebrate = UserCelebration.perform_streak_updates(
        user, course.id, browser_timezone)
    celebrations = {
        'first_section':
        CourseEnrollmentCelebration.should_celebrate_first_section(enrollment),
        'streak_length_to_celebrate':
        streak_length_to_celebrate,
        'streak_discount_experiment_enabled':
        False,
    }

    # We only want to bucket people into the AA-759 experiment if they are going to see the streak celebration
    if streak_length_to_celebrate:
        # We only want to bucket people into the AA-759 experiment
        # if the course has not ended, is upgradeable and the user is not an enterprise learner
        if can_show_streak_discount_experiment_coupon(user, course):
            celebrations[
                'streak_discount_experiment_enabled'] = STREAK_DISCOUNT_EXPERIMENT_FLAG.is_enabled(
                )
    return celebrations
예제 #7
0
 def test_celebration_with_user_configured_timezone(self):
     """
     Check that the _get_now method uses the user's configured timezone
     over the browser timezone that is passed in as a parameter
     """
     set_user_preference(self.user, 'time_zone', 'Asia/Tokyo')
     now = UserCelebration._get_now('America/New_York')  # pylint: disable=protected-access
     assert str(now.tzinfo) == 'Asia/Tokyo'
예제 #8
0
    def test_streak_masquerade(self):
        """ Don't update streak data when masquerading as a specific student """
        # Update streak data when not masquerading
        with mock.patch.object(UserCelebration,
                               '_update_streak') as update_streak_mock:
            for _ in range(1, self.STREAK_LENGTH_TO_CELEBRATE + 1):
                UserCelebration.perform_streak_updates(self.user,
                                                       self.course_key)
                update_streak_mock.assert_called()

        # Don't update streak data when masquerading as a specific student
        with mock.patch(
                'lms.djangoapps.courseware.masquerade.is_masquerading_as_specific_student',
                return_value=True):
            with mock.patch.object(UserCelebration,
                                   '_update_streak') as update_streak_mock:
                for _ in range(1, self.STREAK_LENGTH_TO_CELEBRATE + 1):
                    UserCelebration.perform_streak_updates(
                        self.user, self.course_key)
                    update_streak_mock.assert_not_called()
예제 #9
0
 def test_longest_streak_updates_correctly(self):
     """
     Sample run for a 3 day streak and 1 day break. See last column for explanation.
     +---------+---------------------+--------------------+-------------------------+------------------+---------------------+
     | today   | streak_length       | last_day_of_streak | streak_length_to_celebrate | Note                                |
     +---------+---------------------+--------------------+-------------------------+------------------+---------------------+
     | 2/4/21  | 1                   | 2/4/21             | None                    | longest_streak_ever is 1               |
     | 2/5/21  | 2                   | 2/5/21             | None                    | longest_streak_ever is 2               |
     | 2/6/21  | 3                   | 2/6/21             | 3                       | longest_streak_ever is 3               |
     | 2/7/21  | 4                   | 2/7/21             | None                    | longest_streak_ever is 4               |
     | 2/8/21  | 5                   | 2/8/21             | None                    | longest_streak_ever is 5               |
     | 2/9/21  | 6                   | 2/9/21             | None                    | longest_streak_ever is 6               |
     +---------+---------------------+--------------------+-------------------------+------------------+---------------------+
     """
     now = datetime.datetime.now(UTC)
     for i in range(1, (self.STREAK_LENGTH_TO_CELEBRATE * 2) + 1):
         with freeze_time(now + datetime.timedelta(days=i)):
             UserCelebration.perform_streak_updates(self.user,
                                                    self.course_key)
             assert self.user.celebration.longest_ever_streak == i
예제 #10
0
 def celebrations(self):
     """
     Returns a list of celebrations that should be performed.
     """
     browser_timezone = self.request.query_params.get('browser_timezone', None)
     return {
         'first_section': CourseEnrollmentCelebration.should_celebrate_first_section(self.enrollment_object),
         'streak_length_to_celebrate': UserCelebration.perform_streak_updates(
             self.effective_user, self.course_key, browser_timezone
         ),
     }
예제 #11
0
 def test_celebrate_only_once_with_multiple_calls_on_the_same_day(self):
     """
     Sample run for a 3 day streak and 1 day break. See last column for explanation.
     +---------+---------------------+--------------------+-------------------------+------------------+----------------------------+
     | today   | streak_length       | last_day_of_streak | streak_length_to_celebrate | Note                                       |
     +---------+---------------------+--------------------+-------------------------+------------------+----------------------------+
     | 2/4/21  | 1                   | 2/4/21             | None                    | Day 1 of Streak                               |
     | 2/4/21  | 1                   | 2/4/21             | None                    | Day 1 of Streak                               |
     | 2/5/21  | 2                   | 2/5/21             | None                    | Day 2 of Streak                               |
     | 2/5/21  | 2                   | 2/5/21             | None                    | Day 2 of Streak                               |
     | 2/6/21  | 3                   | 2/6/21             | 3                       | Completed 3 Day Streak so we should celebrate |
     | 2/6/21  | 3                   | 2/6/21             | None                    | Already celebrated this streak.               |
     +---------+---------------------+--------------------+-------------------------+------------------+----------------------------+
     """
     now = datetime.datetime.now(UTC)
     for i in range(1, self.STREAK_LENGTH_TO_CELEBRATE + 1):
         with freeze_time(now + datetime.timedelta(days=i)):
             streak_length_to_celebrate = UserCelebration.perform_streak_updates(
                 self.user, self.course_key)
             assert bool(streak_length_to_celebrate) == (
                 i == self.STREAK_LENGTH_TO_CELEBRATE)
             streak_length_to_celebrate = UserCelebration.perform_streak_updates(
                 self.user, self.course_key)
             assert streak_length_to_celebrate is None
예제 #12
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)
        original_user_is_staff = has_access(request.user, 'staff',
                                            course_key).has_access

        _, request.user = setup_masquerade(
            request,
            course_key,
            staff_access=has_access(request.user, 'staff', course_key),
            reset_masquerade_data=True,
        )

        username = request.user.username if request.user.username else None
        course = course_detail(request, request.user.username, course_key)
        user_is_enrolled = CourseEnrollment.is_enrolled(
            request.user, course_key_string)
        browser_timezone = request.query_params.get('browser_timezone', None)
        celebrations = {
            'streak_length_to_celebrate':
            UserCelebration.perform_streak_updates(request.user, course_key,
                                                   browser_timezone)
        }

        courseware_meta = CoursewareMeta(course_key, request,
                                         request.user.username)
        can_load_courseware = courseware_meta.is_microfrontend_enabled_for_user(
        )

        data = {
            'course_id': course.id,
            'username': username,
            'is_staff': has_access(request.user, 'staff',
                                   course_key).has_access,
            'original_user_is_staff': original_user_is_staff,
            'number': course.display_number_with_default,
            'org': course.display_org_with_default,
            'tabs': get_course_tab_list(request.user, course),
            'title': course.display_name_with_default,
            'is_self_paced': getattr(course, 'self_paced', False),
            'is_enrolled': user_is_enrolled,
            'can_load_courseware': can_load_courseware,
            'celebrations': celebrations,
        }
        context = self.get_serializer_context()
        context['course'] = course
        serializer = self.get_serializer_class()(data, context=context)
        return Response(serializer.data)
예제 #13
0
 def test_celebrate_only_once_in_continuous_streak(self):
     """
     Sample run for a 3 day streak and 1 day break. See last column for explanation.
     +---------+---------------------+--------------------+-------------------------+------------------+------------------+
     | today   | streak_length       | last_day_of_streak | streak_length_to_celebrate | Note                             |
     +---------+---------------------+--------------------+-------------------------+------------------+------------------+
     | 2/4/21  | 1                   | 2/4/21             | None                    | Day 1 of Streak                     |
     | 2/5/21  | 2                   | 2/5/21             | None                    | Day 2 of Streak                     |
     | 2/6/21  | 3                   | 2/6/21             | 3                       | Completed 3 Day Streak so we should celebrate |
     | 2/7/21  | 4                   | 2/7/21             | None                    | Day 4 of Streak                     |
     | 2/8/21  | 5                   | 2/8/21             | None                    | Day 5 of Streak                     |
     | 2/9/21  | 6                   | 2/9/21             | None                    | Day 6 of Streak                     |
     +---------+---------------------+--------------------+-------------------------+------------------+------------------+
     """
     now = datetime.datetime.now(UTC)
     for i in range(1, (self.STREAK_LENGTH_TO_CELEBRATE * 2) + 1):
         with freeze_time(now + datetime.timedelta(days=i)):
             STREAK_LENGTH_TO_CELEBRATE = UserCelebration.perform_streak_updates(
                 self.user, self.course_key)
             assert bool(STREAK_LENGTH_TO_CELEBRATE) == (
                 i == self.STREAK_LENGTH_TO_CELEBRATE)
예제 #14
0
 def test_celebration_with_user_passed_in_timezone(self):
     """
     Check that the _get_now method uses the user's timezone from the browser if none is configured
     """
     now = UserCelebration._get_now('Asia/Tokyo')  # pylint: disable=protected-access
     assert str(now.tzinfo) == 'Asia/Tokyo'