From 56e82034967a1dabf75c2121f983f010d14f89d7 Mon Sep 17 00:00:00 2001 From: Zainab Amir Date: Thu, 28 Jul 2022 18:10:10 +0500 Subject: [PATCH] feat: set default value for is_marketable (#30784) If a user is created using manage_user command, set value for is_marketable attribute to false. VAN-996 --- common/djangoapps/student/models.py | 29 +++++++++++++- .../djangoapps/student/tests/test_models.py | 38 ++++++++++++++++++- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index e77a38c9ea..a9ae649116 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -132,6 +132,7 @@ TRANSITION_STATES = ( (UNENROLLED_TO_UNENROLLED, UNENROLLED_TO_UNENROLLED), (DEFAULT_TRANSITION_STATE, DEFAULT_TRANSITION_STATE) ) +IS_MARKETABLE = 'is_marketable' class AnonymousUserId(models.Model): @@ -848,11 +849,35 @@ def user_post_save_callback(sender, **kwargs): _called_by_management_command = getattr(user, '_called_by_management_command', None) if _called_by_management_command: try: - __ = user.profile + profile = user.profile except UserProfile.DoesNotExist: - UserProfile.objects.create(user=user) + profile = UserProfile.objects.create(user=user) log.info('Created new profile for user: %s', user) + # If user is created using management command, ensure that the user's + # marketable attribute is set (default: False) and an account is created + # on segment. By created an account on segment, it is ensured that data + # will be sent to relevant places like Braze. + if settings.MARKETING_EMAILS_OPT_IN: + UserAttribute.set_user_attribute(user, IS_MARKETABLE, 'false') + + traits = { + 'email': user.email, + 'username': user.username, + 'name': profile.name, + 'age': profile.age or -1, + 'yearOfBirth': profile.year_of_birth or datetime.now(UTC).year, + 'education': profile.level_of_education_display, + 'address': profile.mailing_address, + 'gender': profile.gender_display, + 'country': str(profile.country), + 'is_marketable': False + } + # .. pii: Many pieces of PII are sent to Segment here. Retired directly through Segment API call in Tubular. + # .. pii_types: email_address, username + # .. pii_retirement: third_party + segment.identify(user.id, traits) + # Because `emit_field_changed_events` removes the record of the fields that # were changed, wait to do that until after we've checked them as part of # the condition on whether we want to check for automatic enrollments. diff --git a/common/djangoapps/student/tests/test_models.py b/common/djangoapps/student/tests/test_models.py index e7bf88e46f..fd90a2f37e 100644 --- a/common/djangoapps/student/tests/test_models.py +++ b/common/djangoapps/student/tests/test_models.py @@ -10,7 +10,7 @@ from django.contrib.auth.models import AnonymousUser, User # lint-amnesty, pyli from django.core.cache import cache from django.db.models import signals # pylint: disable=unused-import from django.db.models.functions import Lower -from django.test import TestCase +from django.test import TestCase, override_settings from edx_toggles.toggles.testutils import override_waffle_flag from freezegun import freeze_time from opaque_keys.edx.keys import CourseKey @@ -20,12 +20,14 @@ from common.djangoapps.course_modes.models import CourseMode from common.djangoapps.course_modes.tests.factories import CourseModeFactory from common.djangoapps.student.models import ( ALLOWEDTOENROLL_TO_ENROLLED, + IS_MARKETABLE, AccountRecovery, CourseEnrollment, CourseEnrollmentAllowed, ManualEnrollmentAudit, PendingEmailChange, PendingNameChange, + UserAttribute, UserCelebration, UserProfile ) @@ -781,6 +783,40 @@ class TestUserPostSaveCallback(SharedModuleStoreTestCase): assert actual_course_enrollment.mode == 'verified' assert actual_student.is_active is True + @override_settings(MARKETING_EMAILS_OPT_IN=True) + def test_is_marketable_set_to_false_for_user_created_via_management_command(self): + """ + For users that are created using manage_user.py management command, set the + is_marketable value to 'false'. + """ + expected_traits = { + 'email': 'some.user@example.com', + 'username': 'some_user', + 'name': 'Student Person', + 'age': -1, + 'yearOfBirth': 2022, + 'education': None, + 'address': None, + 'gender': 'Male', + 'country': '', + 'is_marketable': False + } + + user = UserFactory( + username='some_user', + first_name='Student', + last_name='Person', + email='some.user@example.com', + ) + with mock.patch('common.djangoapps.student.models.segment') as mock_segment: + user._called_by_management_command = True # pylint: disable=protected-access + user.save() + + attribute = UserAttribute.objects.filter(user_id=user.id, name=IS_MARKETABLE) + assert attribute + assert mock_segment.identify.call_count == 1 + assert mock_segment.identify.call_args[0] == (user.id, expected_traits) + def _set_up_invited_student(self, course, active=False, enrolled=True, course_mode=''): """ Helper function to create a user in the right state, invite them into the course, and update their