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:
@@ -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?
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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():
|
||||
|
||||
Reference in New Issue
Block a user