def test_get_highlights_without_module(self, mock_get_module):
        mock_get_module.return_value = None

        with self.store.bulk_operations(self.course_key):
            self._create_chapter(highlights='Test highlight')

        with self.assertRaisesRegex(CourseUpdateDoesNotExist, 'Course module .* not found'):
            get_week_highlights(self.user, self.course_key, 1)

        yesterday = datetime.datetime.utcnow() - datetime.timedelta(days=1)
        today = datetime.datetime.utcnow()
        with self.assertRaisesRegex(CourseUpdateDoesNotExist, 'Course module .* not found'):
            get_next_section_highlights(self.user, self.course_key, yesterday, today.date())
Ejemplo n.º 2
0
    def test_get_next_section_highlights(self, mock_duration):
        mock_duration.return_value = datetime.timedelta(days=2)
        yesterday = datetime.datetime.utcnow() - datetime.timedelta(days=1)
        today = datetime.datetime.utcnow()
        tomorrow = datetime.datetime.utcnow() + datetime.timedelta(days=1)
        with self.store.bulk_operations(self.course_key):
            self._create_chapter(  # Week 1
                highlights=[u'a', u'b', u'á'],
            )
            self._create_chapter(  # Week 2
                highlights=[u'skipped a week'],
            )

        self.assertEqual(
            get_next_section_highlights(self.user, self.course_key, yesterday, today.date()),
            ([u'skipped a week'], 2),
        )
        with self.assertRaises(CourseUpdateDoesNotExist):
            get_next_section_highlights(self.user, self.course_key, yesterday, tomorrow.date())
Ejemplo n.º 3
0
    def get_schedules(self):
        course_key = CourseKey.from_string(self.course_id)
        target_date = self.target_datetime.date()
        schedules = get_schedules_with_due_date(course_key, target_date).filter(
            self.experience_filter,
            active=True,
            enrollment__user__is_active=True,
        )

        template_context = get_base_template_context(self.site)
        for schedule in schedules:
            enrollment = schedule.enrollment
            course = schedule.enrollment.course
            user = enrollment.user
            start_date = schedule.start_date
            LOG.info(u'Received a schedule for user {} in course {} for date {}'.format(
                user.username,
                self.course_id,
                target_date,
            ))

            try:
                week_highlights, week_num = get_next_section_highlights(user, course.id, start_date, target_date)
            except CourseUpdateDoesNotExist:
                LOG.warning(
                    u'Weekly highlights for user {} of course {} does not exist or is disabled'.format(
                        user, course.id
                    )
                )
                # continue to the next schedule, don't yield an email for this one
                continue
            unsubscribe_url = None
            if (COURSE_UPDATE_SHOW_UNSUBSCRIBE_WAFFLE_SWITCH.is_enabled() and
                    'bulk_email_optout' in settings.ACE_ENABLED_POLICIES):
                unsubscribe_url = reverse('bulk_email_opt_out', kwargs={
                    'token': UsernameCipher.encrypt(user.username),
                    'course_id': str(enrollment.course_id),
                })

            template_context.update({
                'course_name': course.display_name,
                'course_url': _get_trackable_course_home_url(enrollment.course_id),
                'week_num': week_num,
                'week_highlights': week_highlights,
                # This is used by the bulk email optout policy
                'course_ids': [str(enrollment.course_id)],
                'unsubscribe_url': unsubscribe_url,
            })
            template_context.update(_get_upsell_information_for_schedule(user, schedule))

            yield (user, enrollment.course.closest_released_language, template_context, course.self_paced)
Ejemplo n.º 4
0
    def test_get_next_section_highlights(self, mock_duration):
        # All of the dates chosen here are to make things easy and clean to calculate with date offsets
        # It only goes up to 6 days because we are using two_days_ago as our reference point
        # so 6 + 2 = 8 days for the duration of the course
        mock_duration.return_value = datetime.timedelta(days=8)
        today = datetime.datetime.utcnow()
        two_days_ago = today - datetime.timedelta(days=2)
        two_days = today + datetime.timedelta(days=2)
        three_days = today + datetime.timedelta(days=3)
        four_days = today + datetime.timedelta(days=4)
        six_days = today + datetime.timedelta(days=6)
        with self.store.bulk_operations(self.course_key):
            self._create_chapter(  # Week 1
                highlights=['a', 'b', 'á'], )
            self._create_chapter(  # Week 2
                highlights=['skipped a week'], )
            self._create_chapter(  # Week 3
                highlights=[])
            self._create_chapter(  # Week 4
                highlights=['final week!'])

        self.assertEqual(
            get_next_section_highlights(self.user, self.course_key,
                                        two_days_ago, today.date()),
            (['skipped a week'], 2),
        )
        exception_message = 'Next section [{}] has no highlights for {}'.format(
            'chapter 3', self.course_key)
        with self.assertRaises(CourseUpdateDoesNotExist,
                               msg=exception_message):
            get_next_section_highlights(self.user, self.course_key,
                                        two_days_ago, two_days.date())
        # Returns None, None if the target date does not match any due dates. This is caused by
        # making the mock_duration 8 days and there being only 4 chapters so any odd day will
        # fail to match.
        self.assertEqual(
            get_next_section_highlights(self.user, self.course_key,
                                        two_days_ago, three_days.date()),
            (None, None),
        )
        self.assertEqual(
            get_next_section_highlights(self.user, self.course_key,
                                        two_days_ago, four_days.date()),
            (['final week!'], 4),
        )
        exception_message = 'Last section was reached. There are no more highlights for {}'.format(
            self.course_key)
        with self.assertRaises(CourseUpdateDoesNotExist,
                               msg=exception_message):
            get_next_section_highlights(self.user, self.course_key,
                                        two_days_ago, six_days.date())
Ejemplo n.º 5
0
    def get_schedules(self):
        """
        Grabs possible schedules that could receive a Course Next Section Update and if a
        next section highlight is applicable for the user, yields information needed to
        send the next section highlight email.
        """
        target_date = self.target_datetime.date()
        course_duration = get_expected_duration(self.course_id)
        schedules = Schedule.objects.select_related('enrollment').filter(
            self.experience_filter,
            enrollment__is_active=True,
            enrollment__course_id=self.course_id,
            enrollment__user__is_active=True,
            start_date__gte=target_date - course_duration,
            start_date__lt=target_date,
        )

        template_context = get_base_template_context(self.site)
        for schedule in schedules:
            course = schedule.enrollment.course
            # We don't want to show any updates if the course has ended so we short circuit here.
            if course.end and course.end.date() <= target_date:
                return

            # Next Section Updates are only for Self-paced courses since it uses Personalized
            # Learner Schedule logic. See CourseUpdateResolver for Instructor-paced updates
            if not course.self_paced:
                continue

            user = schedule.enrollment.user
            start_date = max(filter(None, (schedule.start_date, course.start)))
            LOG.info('Received a schedule for user {} in course {} for date {}'.format(
                user.username, self.course_id, target_date,
            ))

            try:
                week_highlights, week_num = get_next_section_highlights(user, course.id, start_date, target_date)
                # (None, None) is returned when there is no section with a due date of the target_date
                if week_highlights is None:
                    continue
            except CourseUpdateDoesNotExist as e:
                log_message = self.log_prefix + ': ' + str(e)
                LOG.warning(log_message)
                # continue to the next schedule, don't yield an email for this one
                continue
            unsubscribe_url = None
            if (COURSE_UPDATE_SHOW_UNSUBSCRIBE_WAFFLE_SWITCH.is_enabled() and
                    'bulk_email_optout' in settings.ACE_ENABLED_POLICIES):
                unsubscribe_url = reverse('bulk_email_opt_out', kwargs={
                    'token': UsernameCipher.encrypt(user.username),
                    'course_id': str(course.id),
                })

            template_context.update({
                'course_name': course.display_name,
                'course_url': _get_trackable_course_home_url(course.id),
                'week_num': week_num,
                'week_highlights': week_highlights,
                # This is used by the bulk email optout policy
                'course_ids': [str(course.id)],
                'unsubscribe_url': unsubscribe_url,
            })
            template_context.update(_get_upsell_information_for_schedule(user, schedule))

            yield (user, course.closest_released_language, template_context)