feat: [AA-950] Productize Streak Discount (#28582)

* feat: [AA-950] Productize Streak Discount

- Change STREAK_DISCOUNT_EXPERIMENT_FLAG to STREAK_DISCOUNT_FLAG
- Remove references to "experiment" and ticket AA-759
- Made flag names more consistent
- Move segment event from get_bucket  to streak calculation
- Streak discount event edx.bi.course.streak_discount_enabled is sent when celebrations are calculated
- Convert LegacyWaffleFlags to WaffleFlags

Co-authored-by: cdeery <cdeery@edx.edu>
This commit is contained in:
Chris Deery
2021-09-01 10:55:32 -04:00
committed by GitHub
parent 5a382d811f
commit 8d682cb447
7 changed files with 50 additions and 60 deletions

View File

@@ -16,8 +16,7 @@ from lms.djangoapps.courseware.toggles import (
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.course_home_api.tests.utils import BaseCourseHomeTests
from lms.djangoapps.experiments.testutils import override_experiment_waffle_flag
from lms.djangoapps.experiments.utils import STREAK_DISCOUNT_EXPERIMENT_FLAG
from lms.djangoapps.experiments.utils import STREAK_DISCOUNT_FLAG
@ddt.ddt
@@ -86,12 +85,12 @@ class CourseHomeMetadataTests(BaseCourseHomeTests):
def test_streak_data_in_response(self):
""" Test that metadata endpoint returns data for the streak celebration """
CourseEnrollment.enroll(self.user, self.course.id, 'audit')
with override_experiment_waffle_flag(STREAK_DISCOUNT_EXPERIMENT_FLAG, active=True):
with override_waffle_flag(STREAK_DISCOUNT_FLAG, active=True):
with mock.patch('common.djangoapps.student.models.UserCelebration.perform_streak_updates', return_value=3):
response = self.client.get(self.url, content_type='application/json')
celebrations = response.json()['celebrations']
assert celebrations['streak_length_to_celebrate'] == 3
assert celebrations['streak_discount_experiment_enabled'] is True
assert celebrations['streak_discount_enabled'] is True
@ddt.data(
# Who has access to MFE courseware?

View File

@@ -11,7 +11,6 @@ from crum import get_current_request
from edx_django_utils.cache import RequestCache
from common.djangoapps.track import segment
from common.djangoapps.course_modes.models import CourseMode
from lms.djangoapps.experiments.stable_bucketing import stable_bucketing_hash_group
from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
@@ -266,21 +265,6 @@ class ExperimentWaffleFlag(CourseWaffleFlag):
# Mark that we've recorded this bucketing, so that we don't do it again this session
request.session[session_key] = True
# Temporary event for AA-759 experiment
if course_key and self._experiment_name == 'discount_experiment_AA759':
modes_dict = CourseMode.modes_for_course_dict(course_id=course_key, include_expired=False)
verified_mode = modes_dict.get('verified', None)
if verified_mode:
segment.track(
user_id=user.id,
event_name='edx.bi.experiment.AA759.bucketed',
properties={
'course_id': str(course_key),
'bucket': bucket,
'sku': verified_mode.sku,
}
)
return self._cache_bucket(experiment_name, bucket)
def is_enabled(self, course_key=None):

View File

@@ -7,7 +7,7 @@ import logging
from decimal import Decimal
from django.utils.timezone import now
from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace
from edx_toggles.toggles import WaffleFlag
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
@@ -17,7 +17,6 @@ from common.djangoapps.student.models import CourseEnrollment
from lms.djangoapps.commerce.utils import EcommerceService
from lms.djangoapps.courseware.access import has_staff_access_to_preview_mode
from lms.djangoapps.courseware.utils import can_show_verified_upgrade, verified_upgrade_deadline_link
from lms.djangoapps.experiments.flags import ExperimentWaffleFlag
from openedx.core.djangoapps.catalog.utils import get_programs
from openedx.core.djangoapps.django_comment_common.models import Role
from openedx.core.djangoapps.schedules.models import Schedule
@@ -28,8 +27,6 @@ logger = logging.getLogger(__name__)
# TODO: clean up as part of REVEM-199 (START)
experiments_namespace = LegacyWaffleFlagNamespace(name='experiments')
# .. toggle_name: experiments.add_programs
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
@@ -39,10 +36,9 @@ experiments_namespace = LegacyWaffleFlagNamespace(name='experiments')
# .. toggle_target_removal_date: None
# .. toggle_tickets: REVEM-63, REVEM-198
# .. toggle_warnings: This temporary feature toggle does not have a target removal date.
PROGRAM_INFO_FLAG = LegacyWaffleFlag(
waffle_namespace=experiments_namespace,
flag_name='add_programs',
module_name=__name__,
PROGRAM_INFO_FLAG = WaffleFlag(
'experiments.add_programs',
__name__,
)
# .. toggle_name: experiments.add_dashboard_info
@@ -54,7 +50,7 @@ PROGRAM_INFO_FLAG = LegacyWaffleFlag(
# .. toggle_target_removal_date: None
# .. toggle_tickets: REVEM-118
# .. toggle_warnings: This temporary feature toggle does not have a target removal date.
DASHBOARD_INFO_FLAG = LegacyWaffleFlag(experiments_namespace, 'add_dashboard_info', __name__)
DASHBOARD_INFO_FLAG = WaffleFlag('experiments.add_dashboard_info', __name__)
# TODO END: clean up as part of REVEM-199 (End)
# TODO: Clean up as part of REV-1205 (START)
@@ -67,27 +63,24 @@ DASHBOARD_INFO_FLAG = LegacyWaffleFlag(experiments_namespace, 'add_dashboard_inf
# .. toggle_target_removal_date: None
# .. toggle_tickets: REV-1205
# .. toggle_warnings: This temporary feature toggle does not have a target removal date.
UPSELL_TRACKING_FLAG = LegacyWaffleFlag(
waffle_namespace=experiments_namespace,
flag_name='add_upsell_tracking',
module_name=__name__,
UPSELL_TRACKING_FLAG = WaffleFlag(
'experiments.add_upsell_tracking',
__name__,
)
# TODO END: Clean up as part of REV-1205 (End)
# .. toggle_name: streak_celebration.discount_experiment_AA759
# .. toggle_implementation: ExperimentWaffleFlag
# .. toggle_name: streak_celebration.streak_discount_enabled
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This experiment flag enables an engagement discount incentive message.
# .. toggle_description: This flag enables an engagement discount incentive message.
# .. toggle_warnings: This flag depends on the streak celebration feature being enabled
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2021-05-05
# .. toggle_target_removal_date: 2021-07-05
# .. toggle_tickets: https://openedx.atlassian.net/browse/AA-759
STREAK_DISCOUNT_EXPERIMENT_FLAG = ExperimentWaffleFlag(
LegacyWaffleFlagNamespace(name='streak_celebration'),
'discount_experiment_AA759',
# .. toggle_use_cases: opt_out, open_edx
# .. toggle_creation_date: 2021-08-26
# .. toggle_target_removal_date: None
# .. toggle_tickets: https://openedx.atlassian.net/browse/AA-950
STREAK_DISCOUNT_FLAG = WaffleFlag(
'streak_celebration.streak_discount_enabled',
__name__,
use_course_aware_bucketing=False
)

View File

@@ -88,7 +88,7 @@
"celebrations": {
"first_section": false,
"streak_length_to_celebrate": null,
"streak_discount_experiment_enabled": false
"streak_discount_enabled": false
},
"user_has_passing_grade": false,
"course_exit_page_is_active": false,
@@ -223,7 +223,7 @@
"$.body.celebrations.irst_section": {
"match": "type"
},
"$.body.celebrations.streak_discount_experiment_enabled": {
"$.body.celebrations.streak_discount_enabled": {
"match": "type"
},
"$.body.user_has_passing_grade": {

View File

@@ -29,8 +29,7 @@ from lms.djangoapps.courseware.toggles import (
COURSEWARE_MICROFRONTEND_SPECIAL_EXAMS,
COURSEWARE_MICROFRONTEND_PROCTORED_EXAMS,
)
from lms.djangoapps.experiments.testutils import override_experiment_waffle_flag
from lms.djangoapps.experiments.utils import STREAK_DISCOUNT_EXPERIMENT_FLAG
from lms.djangoapps.experiments.utils import STREAK_DISCOUNT_FLAG
from lms.djangoapps.verify_student.services import IDVerificationService
from common.djangoapps.student.models import (
CourseEnrollment, CourseEnrollmentCelebration
@@ -302,12 +301,12 @@ class CourseApiTestViews(BaseCoursewareTests, MasqueradeMixin):
def test_streak_data_in_response(self):
""" Test that metadata endpoint returns data for the streak celebration """
CourseEnrollment.enroll(self.user, self.course.id, 'audit')
with override_experiment_waffle_flag(STREAK_DISCOUNT_EXPERIMENT_FLAG, active=True):
with override_waffle_flag(STREAK_DISCOUNT_FLAG, active=True):
with mock.patch('common.djangoapps.student.models.UserCelebration.perform_streak_updates', return_value=3):
response = self.client.get(self.url, content_type='application/json')
celebrations = response.json()['celebrations']
assert celebrations['streak_length_to_celebrate'] == 3
assert celebrations['streak_discount_experiment_enabled'] is True
assert celebrations['streak_discount_enabled'] is True
@ddt.data(
(False, False),

View File

@@ -7,9 +7,10 @@ from babel.numbers import get_currency_symbol
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.student.models import CourseEnrollmentCelebration, UserCelebration
from lms.djangoapps.courseware.utils import can_show_verified_upgrade, verified_upgrade_deadline_link
from lms.djangoapps.experiments.utils import STREAK_DISCOUNT_EXPERIMENT_FLAG
from lms.djangoapps.experiments.utils import STREAK_DISCOUNT_FLAG
from openedx.features.course_duration_limits.access import get_user_course_expiration_date
from openedx.features.discounts.applicability import can_show_streak_discount_experiment_coupon
from openedx.features.discounts.applicability import can_show_streak_discount_coupon
from common.djangoapps.track import segment
def get_celebrations_dict(user, enrollment, course, browser_timezone):
@@ -20,7 +21,7 @@ def get_celebrations_dict(user, enrollment, course, browser_timezone):
return {
'first_section': False,
'streak_length_to_celebrate': None,
'streak_discount_experiment_enabled': False,
'streak_discount_enabled': False,
}
streak_length_to_celebrate = UserCelebration.perform_streak_updates(
@@ -29,15 +30,29 @@ def get_celebrations_dict(user, enrollment, course, browser_timezone):
celebrations = {
'first_section': CourseEnrollmentCelebration.should_celebrate_first_section(enrollment),
'streak_length_to_celebrate': streak_length_to_celebrate,
'streak_discount_experiment_enabled': False,
'streak_discount_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
# 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_experiment_coupon(user, course):
celebrations['streak_discount_experiment_enabled'] = STREAK_DISCOUNT_EXPERIMENT_FLAG.is_enabled(course.id)
if can_show_streak_discount_coupon(user, course):
celebrations['streak_discount_enabled'] = STREAK_DISCOUNT_FLAG.is_enabled()
# 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:
segment.track(
user_id=user.id,
event_name='edx.bi.course.streak_discount_enabled',
properties={
'course_id': str(course_key),
'sku': verified_mode.sku,
}
)
return celebrations

View File

@@ -83,10 +83,10 @@ def get_discount_expiration_date(user, course):
return discount_expiration_date
def can_show_streak_discount_experiment_coupon(user, course):
def can_show_streak_discount_coupon(user, course):
"""
Check whether this combination of user and course
can receive the AA-759 experiment discount.
can receive the streak discount.
"""
# Course end date needs to be in the future
if course.has_ended():