diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 57949d8fbc..a101e749ad 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -1488,6 +1488,13 @@ class CourseEnrollment(models.Model): 'run': self.course_id.run, 'mode': self.mode, } + # 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-804: remove the properties duplication in the event traits. + segment_traits = dict(segment_properties) + # Add course_title to the traits, as it is used by Hubspot filters + segment_traits['course_title'] = self.course_overview.display_name if self.course_overview else None if event_name == EVENT_NAME_ENROLLMENT_ACTIVATED: segment_properties['email'] = self.user.email # This next property is for an experiment, see method's comments for more information @@ -1495,7 +1502,7 @@ class CourseEnrollment(models.Model): self.course_id) with tracker.get_tracker().context(event_name, context): tracker.emit(event_name, data) - segment.track(self.user_id, event_name, segment_properties) + segment.track(self.user_id, event_name, segment_properties, traits=segment_traits) except Exception: # pylint: disable=broad-except if event_name and self.course_id: diff --git a/common/djangoapps/student/tests/test_enrollment.py b/common/djangoapps/student/tests/test_enrollment.py index facd68d0e9..1b6e074746 100644 --- a/common/djangoapps/student/tests/test_enrollment.py +++ b/common/djangoapps/student/tests/test_enrollment.py @@ -166,6 +166,23 @@ class EnrollmentTest(UrlResetMixin, SharedModuleStoreTestCase): assert mock_segment.track.call_args[0][1] == 'edx.course.enrollment.activated' assert mock_segment.track.call_args[0][2]['external_course_updates'] == value + def test_enrollment_properties_in_segment_traits(self): + with patch('common.djangoapps.student.models.segment') as mock_segment: + enrollment = CourseEnrollment.enroll(self.user, self.course.id) + assert mock_segment.track.call_count == 1 + assert mock_segment.track.call_args[0][1] == 'edx.course.enrollment.activated' + traits = mock_segment.track.call_args[1]['traits'] + assert traits['course_title'] == self.course.display_name + assert traits['mode'] == 'audit' + + with patch('common.djangoapps.student.models.segment') as mock_segment: + enrollment.update_enrollment(mode='verified') + assert mock_segment.track.call_count == 1 + assert mock_segment.track.call_args[0][1] == 'edx.course.enrollment.mode_changed' + traits = mock_segment.track.call_args[1]['traits'] + assert traits['course_title'] == self.course.display_name + assert traits['mode'] == 'verified' + @patch.dict(settings.FEATURES, {'ENABLE_MKTG_EMAIL_OPT_IN': True}) @patch('openedx.core.djangoapps.user_api.preferences.api.update_email_opt_in') @ddt.data( diff --git a/common/djangoapps/track/segment.py b/common/djangoapps/track/segment.py index fd24b37d53..5411df7b38 100644 --- a/common/djangoapps/track/segment.py +++ b/common/djangoapps/track/segment.py @@ -16,7 +16,7 @@ from eventtracking import tracker from six.moves.urllib.parse import urlunsplit -def track(user_id, event_name, properties=None, context=None): +def track(user_id, event_name, properties=None, context=None, traits=None): """ Wrapper for emitting Segment track event, including augmenting context information from middleware. """ @@ -59,6 +59,9 @@ def track(user_id, event_name, properties=None, context=None): if page is not None and 'url' not in segment_context['page']: segment_context['page']['url'] = page + if traits: + segment_context['traits'] = traits + analytics.track(user_id, event_name, properties, segment_context) diff --git a/openedx/core/djangoapps/user_authn/views/register.py b/openedx/core/djangoapps/user_authn/views/register.py index 860f8a4cbc..0ae77188a5 100644 --- a/openedx/core/djangoapps/user_authn/views/register.py +++ b/openedx/core/djangoapps/user_authn/views/register.py @@ -341,20 +341,29 @@ def _track_user_registration(user, profile, params, third_party_provider): # .. 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), + } + # 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", - { - '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), - }, + properties=properties, + traits=segment_traits, )