Exemple #1
0
    def schedules_for_bin(self):
        week_num = abs(self.day_offset) // 7
        schedules = self.get_schedules_with_target_date_by_bin_and_orgs(
            order_by='enrollment__course', )

        template_context = get_base_template_context(self.site)
        for schedule in schedules:
            enrollment = schedule.enrollment
            course = schedule.enrollment.course
            user = enrollment.user

            # (Weekly) Course Updates are only for Instructor-paced courses.
            # See CourseNextSectionUpdate for Self-paced updates.
            if course.self_paced:
                continue

            try:
                week_highlights = get_week_highlights(user,
                                                      enrollment.course_id,
                                                      week_num)
            except CourseUpdateDoesNotExist:
                LOG.warning(
                    'Weekly highlights for user {} in week {} of course {} does not exist or is disabled'
                    .format(user, week_num, enrollment.course_id))
                # continue to the next schedule, don't yield an email for this one
            else:
                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':
                    schedule.enrollment.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,
                       schedule.enrollment.course.closest_released_language,
                       template_context)
Exemple #2
0
 def assertPrefValid(self, user):
     """Ensure that the correct preference for the user is persisted"""
     pref = UserPreference.objects.get(user=user, key=NOTIFICATION_PREF_KEY)
     self.assertTrue(pref)  # check exists and only 1 (.get)
     # now coerce username to utf-8 encoded str, since we test with non-ascii unicdoe above and
     # the unittest framework has hard time coercing to unicode.
     # decrypt also can't take a unicode input, so coerce its input to str
     self.assertEqual(six.binary_type(user.username.encode('utf-8')), UsernameCipher().decrypt(str(pref.value)))
    def setUp(self):
        super().setUp()
        self.user = UserFactory.create(username="******", email='*****@*****.**')
        self.course = CourseFactory.create(run='testcourse1', display_name='Test Course Title')
        self.token = UsernameCipher.encrypt('testuser1')
        self.request_factory = RequestFactory()
        self.url = reverse('bulk_email_opt_out', args=[self.token, str(self.course.id)])

        # Ensure we start with no opt-out records
        assert Optout.objects.count() == 0
Exemple #4
0
def get_unsubscribed_link(username, course_id):
    """

    :param username: username
    :param course_id:
    :return: AES encrypted token based on the user email
    """
    token = UsernameCipher.encrypt(username)
    optout_url = reverse('bulk_email_opt_out', kwargs={'token': token, 'course_id': course_id})
    url = '{base_url}{optout_url}'.format(base_url=settings.LMS_ROOT_URL, optout_url=optout_url)
    return url
Exemple #5
0
def get_unsubscribed_link(username, course_id):
    """

    :param username: username
    :param course_id:
    :return: AES encrypted token based on the user email
    """
    lms_root_url = configuration_helpers.get_value('LMS_ROOT_URL', settings.LMS_ROOT_URL)
    token = UsernameCipher.encrypt(username)
    optout_url = reverse('bulk_email_opt_out', kwargs={'token': token, 'course_id': course_id})
    url = f'{lms_root_url}{optout_url}'
    return url
Exemple #6
0
    def setUp(self):
        super(OptOutEmailUpdatesViewTest, self).setUp()
        self.user = UserFactory.create(username="******")
        self.token = UsernameCipher.encrypt('testuser1')
        self.request_factory = RequestFactory()
        self.course = CourseFactory.create(run='testcourse1',
                                           display_name='Test Course Title')
        self.url = reverse('bulk_email_opt_out',
                           args=[self.token,
                                 text_type(self.course.id)])

        # Ensure we start with no opt-out records
        self.assertEqual(Optout.objects.count(), 0)
Exemple #7
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)
Exemple #8
0
    def setUp(self):
        super(OptOutEmailUpdatesViewTest, self).setUp()  # lint-amnesty, pylint: disable=super-with-arguments
        self.user = UserFactory.create(username="******",
                                       email='*****@*****.**')
        self.course = CourseFactory.create(run='testcourse1',
                                           display_name='Test Course Title')
        self.token = UsernameCipher.encrypt('testuser1')
        self.request_factory = RequestFactory()
        self.url = reverse('bulk_email_opt_out',
                           args=[self.token,
                                 text_type(self.course.id)])

        # Ensure we start with no opt-out records
        assert Optout.objects.count() == 0
Exemple #9
0
def opt_out_email_updates(request, token, course_id):
    """
    A view that let users opt out of any email updates.

    This meant is meant to be the target of an opt-out link or button.
    The `token` parameter must decrypt to a valid username.
    The `course_id` is the string course key of any course.

    Raises a 404 if there are any errors parsing the input.
    """
    try:
        username = UsernameCipher().decrypt(token.encode())
        user = User.objects.get(username=username)
        course_key = CourseKey.from_string(course_id)
        course = get_course_by_id(course_key, depth=0)
    except UnicodeDecodeError:
        raise Http404("base64url")
    except UsernameDecryptionException as exn:
        raise Http404(text_type(exn))
    except User.DoesNotExist:
        raise Http404("username")
    except InvalidKeyError:
        raise Http404("course")

    context = {
        'course': course,
        'cancelled': False,
        'confirmed': False,
    }

    if request.method == 'POST':
        if request.POST.get('submit') == 'confirm':
            Optout.objects.get_or_create(user=user, course_id=course.id)
            log.info(
                u"User %s (%s) opted out of receiving emails from course %s",
                user.username,
                user.email,
                course_id,
            )
            context['confirmed'] = True
        else:
            context['cancelled'] = True

    return render_to_response('bulk_email/unsubscribe.html', context)
Exemple #10
0
def opt_out_email_updates(request, token, course_id):
    """
    A view that let users opt out of any email updates.

    This meant is meant to be the target of an opt-out link or button.
    The `token` parameter must decrypt to a valid username.
    The `course_id` is the string course key of any course.

    Raises a 404 if there are any errors parsing the input.
    """
    try:
        username = UsernameCipher().decrypt(token).decode("utf-8")
        user = User.objects.get(username=username)
        course_key = CourseKey.from_string(course_id)
        course = get_course_by_id(course_key, depth=0)
    except UnicodeDecodeError:
        raise Http404("base64url")  # lint-amnesty, pylint: disable=raise-missing-from
    except UsernameDecryptionException as exn:
        raise Http404(text_type(exn))  # lint-amnesty, pylint: disable=raise-missing-from
    except User.DoesNotExist:
        raise Http404("username")  # lint-amnesty, pylint: disable=raise-missing-from
    except InvalidKeyError:
        raise Http404("course")  # lint-amnesty, pylint: disable=raise-missing-from

    unsub_check = request.POST.get('unsubscribe', False)
    context = {
        'course': course,
        'unsubscribe': unsub_check
    }

    if request.method == 'GET':
        return render_to_response('bulk_email/confirm_unsubscribe.html', context)

    if request.method == 'POST' and unsub_check:
        Optout.objects.get_or_create(user=user, course_id=course_key)
        log.info(
            u"User %s (%s) opted out of receiving emails from course %s",
            user.username,
            user.email,
            course_id,
        )

    return render_to_response('bulk_email/unsubscribe_success.html', context)
Exemple #11
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)