def test_call_with_context(self): provided_context = {sentinel.context_key: sentinel.context_value} segment.identify(sentinel.user_id, self.properties, provided_context) self.assertTrue(self.mock_segment_identify.called) args, kwargs = self.mock_segment_identify.call_args # lint-amnesty, pylint: disable=unused-variable self.assertEqual((sentinel.user_id, self.properties, provided_context), args)
def _track_user_login(user, request): """ Sends a tracking event for a successful login. """ # .. pii: Username and email 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, { 'email': request.POST.get('email'), 'username': user.username }, { # Disable MailChimp because we don't want to update the user's email # and username in MailChimp on every page load. We only need to capture # this data on registration/activation. 'MailChimp': False }) segment.track( user.id, "edx.bi.user.account.authenticated", { 'category': "conversion", 'label': request.POST.get('course_id'), 'provider': None }, )
def test_call_with_context(self): provided_context = {sentinel.context_key: sentinel.context_value} segment.identify(sentinel.user_id, self.properties, provided_context) self.assertTrue(self.mock_segment_identify.called) args, kwargs = self.mock_segment_identify.call_args self.assertEqual((sentinel.user_id, self.properties, provided_context), args)
def user_fields_changed( user=None, table=None, changed_fields: Optional[Dict[str, Tuple[Any, Any]]] = None, **_kwargs, ): """ Update a collection of user profile fields in segment when they change in the database Args: user: The user object for the user being changed table: The name of the table being updated changed_fields: A mapping from changed field name to old and new values. """ fields = { field: new_value for (field, (old_value, new_value)) in changed_fields.items() } # This mirrors the logic in ./views/register.py:_track_user_registration if table == 'auth_userprofile': if 'gender' in fields and fields['gender']: fields['gender'] = dict( UserProfile.GENDER_CHOICES)[fields['gender']] if 'country' in fields: fields['country'] = str(fields['country']) if 'level_of_education' in fields and fields['level_of_education']: fields['education'] = dict(UserProfile.LEVEL_OF_EDUCATION_CHOICES)[ fields['level_of_education']] if 'year_of_birth' in fields: fields['yearOfBirth'] = fields.pop('year_of_birth') if 'mailing_address' in fields: fields['address'] = fields.pop('mailing_address') segment.identify(user.id, fields)
def _track_user_registration(user, profile, params, third_party_provider): """ Track the user's registration. """ if hasattr(settings, 'LMS_SEGMENT_KEY') and settings.LMS_SEGMENT_KEY: identity_args = [ user.id, { 'email': user.email, 'username': user.username, 'name': profile.name, # Mailchimp requires the age & yearOfBirth to be integers, we send a sane integer default if falsey. 'age': profile.age or -1, 'yearOfBirth': profile.year_of_birth or datetime.datetime.now(UTC).year, 'education': profile.level_of_education_display, 'address': profile.mailing_address, 'gender': profile.gender_display, 'country': text_type(profile.country), } ] # .. pii: Many pieces of PII are sent to Segment here. Retired directly through Segment API call in Tubular. # .. pii_types: email_address, username, name, birth_date, location, gender # .. pii_retirement: third_party segment.identify(*identity_args) segment.track( user.id, "edx.bi.user.account.registered", { 'category': 'conversion', # ..pii: Learner email is sent to Segment in following line and will be associated with analytics data. 'email': user.email, 'label': params.get('course_id'), 'provider': third_party_provider.name if third_party_provider else None }, )
def emit_event_for_bu_courses_progress(user, status): """ Emit event to update the progress of Business Unit courses Arguments: user (User): User for which to emit the BU courses progress update status (str): Progress of the bu courses """ segment.identify(user.id, {'businessLineCoursesStatus': status})
def _track_user_registration(user, profile, params, third_party_provider, registration): """ Track the user's registration. """ if hasattr(settings, 'LMS_SEGMENT_KEY') and settings.LMS_SEGMENT_KEY: identity_args = [ user.id, { 'email': user.email, 'username': user.username, 'name': profile.name, # Mailchimp requires the age & yearOfBirth to be integers, we send a sane integer default if falsey. 'age': profile.age or -1, 'yearOfBirth': profile.year_of_birth or datetime.datetime.now(UTC).year, 'education': profile.level_of_education_display, 'address': profile.mailing_address, 'gender': profile.gender_display, 'country': str(profile.country), 'email_subscribe': 'unsubscribed' if settings.MARKETING_EMAILS_OPT_IN and params.get('marketing_emails_opt_in') == 'false' else 'subscribed', } ] # .. pii: Many pieces of PII are sent to Segment here. Retired directly through Segment API call in Tubular. # .. pii_types: email_address, username, name, birth_date, location, gender # .. pii_retirement: third_party segment.identify(*identity_args) properties = { 'category': 'conversion', # ..pii: Learner email is sent to Segment in following line and will be associated with analytics data. 'email': user.email, 'label': params.get('course_id'), 'provider': third_party_provider.name if third_party_provider else None, 'is_gender_selected': bool(profile.gender_display), 'is_year_of_birth_selected': bool(profile.year_of_birth), 'is_education_selected': bool(profile.level_of_education_display), 'is_goal_set': bool(profile.goals), 'total_registration_time': round(float(params.get('totalRegistrationTime', '0'))), 'activation_key': registration.activation_key if registration else None, } # VAN-738 - added below properties to experiment marketing emails opt in/out events on Braze. if params.get('marketing_emails_opt_in') and settings.MARKETING_EMAILS_OPT_IN: properties['marketing_emails_opt_in'] = params.get('marketing_emails_opt_in') == 'true' # DENG-803: For segment events forwarded along to Hubspot, duplicate the `properties` section of # the event payload into the `traits` section so that they can be received. This is a temporary # fix until we implement this behavior outside of the LMS. # TODO: DENG-805: remove the properties duplication in the event traits. segment_traits = dict(properties) segment_traits['user_id'] = user.id segment_traits['joined_date'] = user.date_joined.strftime("%Y-%m-%d") segment.track( user.id, "edx.bi.user.account.registered", properties=properties, traits=segment_traits, )
def emit_registration_event(user): """ Track and Identify required attributes when a new user is created Arguments: user (User): User to track """ user_properties = { 'email': user.email, 'username': user.username, 'name': user.profile.name, 'city': user.profile.city, } segment.identify(user.id, user_properties) segment.track(user.id, USER_REGISTRATION_EVENT, user_properties)
def emit_course_status_event_for_program_prereq_courses(user_id, course, course_status): """ Emit the progress status for Program prereq courses i.e Started or Completed Arguments: user_id (id): Id of the user for which to emit the Omni course progress update course (CourseOverview): Course for which to emit the event for course_status (str): The status of the given course """ course_name_status_key = f'{course.id.course}Status' segment.identify(user_id, {course_name_status_key: course_status}) course_update_event_name = f'ProgramPrereq{course_status}' segment.track(user_id, course_update_event_name, { 'course': course.id.course, 'label': text_type(course.id), 'org': course.id.org, 'run': course.id.run, })
def emit_user_info_update(user, application_step): """ Emit an event to update the user information at Segment after different steps of the application. Arguments: user (User): User for which to emit the event application_step (str): Step of the application to update the user information for """ if application_step == CONTACT_INFO: attributes = { 'isSaudi': user.extended_profile.saudi_national, 'age': user.extended_profile.age, 'heardAboutUs': user.extended_profile.hear_about_omni, } elif application_step == EXPERIENCE: first_education = user.application.educations.first() education_start_date, education_end_date = _get_start_date_and_end_date(first_education) attributes = { 'school': first_education.name_of_school, 'degree': first_education.get_degree_display(), 'educationStartDate': education_start_date, 'educationEndDate': education_end_date, } first_experience = user.application.workexperiences.first() if first_experience: experience_start_date, experience_end_date = _get_start_date_and_end_date(first_experience) attributes.update({ 'workOrganization': first_experience.name_of_organization, 'jobTitle': first_experience.job_position_title, 'workStartDate': experience_start_date, 'workEndDate': experience_end_date }) else: attributes = { 'businessLine': user.application.business_line.title } segment.identify(user.id, attributes) segment.track(user.id, APPLICATION_EVENTS.get(application_step), attributes)
def emit_application_progress(user): """ Emit the progress for the application of the given user. The progress statuses can be Started, Submitted, Waitlisted and Accepted. Arguments: user (User): User to emit progress update for """ application_progress = user.application_hub.application_progress application_status = APPLICATION_PROGRESS.get(application_progress, APPLICATION_PROGRESS[APPLICATION_STARTED]) segment.identify(user.id, {'applicationStatus': application_status}) application_event = APPLICATION_EVENTS.get(application_progress, APPLICATION_EVENTS[APPLICATION_STARTED]) event_properties = { 'applicationStatus': application_status } if hasattr(user, 'application'): event_properties['applicationId'] = user.application.id segment.track(user.id, application_event, event_properties)
def email_marketing_user_fields_changed( sender, # pylint: disable=unused-argument user=None, table=None, changed_fields: Optional[Dict[str, Tuple[Any, Any]]] = None, **kwargs): """ Update a collection of user profile fields Args: sender: Not used user: The user object for the user being changed table: The name of the table being updated changed_fields: A mapping from changed field name to old and new values. kwargs: Not used """ fields = { field: new_value for (field, (old_value, new_value)) in changed_fields.items() } # This mirrors the logic in openedx/core/djangoapps/user_authn/views/register.py:_track_user_registration if table == 'auth_userprofile': if 'gender' in fields and fields['gender']: fields['gender'] = dict( UserProfile.GENDER_CHOICES)[fields['gender']] if 'country' in fields: fields['country'] = str(fields['country']) if 'level_of_education' in fields and fields['level_of_education']: fields['level_of_education'] = dict( UserProfile.LEVEL_OF_EDUCATION_CHOICES)[ fields['level_of_education']] if 'year_of_birth' in fields: fields['yearOfBirth'] = fields.pop('year_of_birth') if 'mailing_address' in fields: fields['address'] = fields.pop('mailing_address') segment.identify(user.id, fields)
def test_normal_call(self): segment.identify(sentinel.user_id, self.properties) assert self.mock_segment_identify.called args, kwargs = self.mock_segment_identify.call_args # lint-amnesty, pylint: disable=unused-variable assert (sentinel.user_id, self.properties, {}) == args
def test_null_key(self): segment.identify(sentinel.user_id, self.properties) assert not self.mock_segment_identify.called
def test_normal_call(self): segment.identify(sentinel.user_id, self.properties) self.assertTrue(self.mock_segment_identify.called) args, kwargs = self.mock_segment_identify.call_args # lint-amnesty, pylint: disable=unused-variable self.assertEqual((sentinel.user_id, self.properties, {}), args)
def test_missing_key(self): segment.identify(sentinel.user_id, self.properties) self.assertFalse(self.mock_segment_identify.called)
def test_normal_call(self): segment.identify(sentinel.user_id, self.properties) self.assertTrue(self.mock_segment_identify.called) args, kwargs = self.mock_segment_identify.call_args self.assertEqual((sentinel.user_id, self.properties, {}), args)