Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split GA4 event product_download into product specific events Fix #14072 #14083

Merged
merged 1 commit into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bedrock/mozorg/templatetags/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ def _get_adjust_link(adjust_url, app_store_url, google_play_url, redirect, local
if redirect_url:
link += "?redirect=" + quote(redirect_url, safe="") + "&" + params + "&mz_pl=" + redirect
else:
link += "?" + params + "&mz_pl=mobile"
link += "?" + params

return link

Expand All @@ -737,7 +737,7 @@ def firefox_adjust_url(ctx, redirect, adgroup, creative=None):
play_store_url = settings.GOOGLE_PLAY_FIREFOX_LINK
locale = getattr(ctx["request"], "locale", "en-US")

return _get_adjust_link(adjust_url, app_store_url, play_store_url, redirect, locale, "firefox", adgroup, creative)
return _get_adjust_link(adjust_url, app_store_url, play_store_url, redirect, locale, "firefox_mobile", adgroup, creative)


@library.global_function
Expand Down
16 changes: 8 additions & 8 deletions bedrock/mozorg/tests/test_helper_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -898,38 +898,38 @@ def test_firefox_ios_adjust_url(self):
assert (
self._render("en-US", "ios", "test-page") == "https://app.adjust.com/2uo1qc?redirect="
"https%3A%2F%2Fitunes.apple.com%2Fus%2Fapp%2Ffirefox-private-safe-browser%2Fid989804926"
"&campaign=www.mozilla.org&adgroup=test-page&mz_pr=firefox&mz_pl=ios"
"&campaign=www.mozilla.org&adgroup=test-page&mz_pr=firefox_mobile&mz_pl=ios"
)

def test_firefox_ios_adjust_url_invalid_country(self):
"""Firefox for mobile with an App Store URL redirect with an unsupported country"""
assert (
self._render("zz", "ios", "test-page") == "https://app.adjust.com/2uo1qc?redirect="
"https%3A%2F%2Fitunes.apple.com%2Fapp%2Ffirefox-private-safe-browser%2Fid989804926"
"&campaign=www.mozilla.org&adgroup=test-page&mz_pr=firefox&mz_pl=ios"
"&campaign=www.mozilla.org&adgroup=test-page&mz_pr=firefox_mobile&mz_pl=ios"
)

def test_firefox_ios_adjust_url_creative(self):
"""Firefox for mobile with an App Store URL redirect and creative param"""
assert (
self._render("de", "ios", "test-page", "experiment-name") == "https://app.adjust.com/2uo1qc?redirect="
"https%3A%2F%2Fitunes.apple.com%2Fde%2Fapp%2Ffirefox-private-safe-browser%2Fid989804926"
"&campaign=www.mozilla.org&adgroup=test-page&creative=experiment-name&mz_pr=firefox&mz_pl=ios"
"&campaign=www.mozilla.org&adgroup=test-page&creative=experiment-name&mz_pr=firefox_mobile&mz_pl=ios"
)

def test_firefox_android_adjust_url(self):
"""Firefox for mobile with a Play Store redirect"""
assert (
self._render("en-US", "android", "test-page") == "https://app.adjust.com/2uo1qc?redirect="
"https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dorg.mozilla.firefox"
"&campaign=www.mozilla.org&adgroup=test-page&mz_pr=firefox&mz_pl=android"
"&campaign=www.mozilla.org&adgroup=test-page&mz_pr=firefox_mobile&mz_pl=android"
)

def test_firefox_no_redirect_adjust_url(self):
"""Firefox for mobile with no redirect"""
assert (
self._render("en-US", None, "test-page") == "https://app.adjust.com/2uo1qc?"
"campaign=www.mozilla.org&adgroup=test-page&mz_pr=firefox&mz_pl=mobile"
"campaign=www.mozilla.org&adgroup=test-page&mz_pr=firefox_mobile"
)


Expand Down Expand Up @@ -981,7 +981,7 @@ def test_focus_no_redirect_adjust_url(self):
"""Firefox Focus for mobile with no redirect"""
assert (
self._render("en-US", None, "test-page") == "https://app.adjust.com/b8s7qo?"
"campaign=www.mozilla.org&adgroup=test-page&mz_pr=focus&mz_pl=mobile"
"campaign=www.mozilla.org&adgroup=test-page&mz_pr=focus"
)

def test_klar_ios_adjust_url(self):
Expand Down Expand Up @@ -1049,7 +1049,7 @@ def test_lockwise_no_redirect_adjust_url(self):
"""Firefox Lockwise for mobile with no redirect"""
assert (
self._render("en-US", None, "test-page") == "https://app.adjust.com/6tteyjo?"
"campaign=www.mozilla.org&adgroup=test-page&mz_pr=lockwise&mz_pl=mobile"
"campaign=www.mozilla.org&adgroup=test-page&mz_pr=lockwise"
)


Expand Down Expand Up @@ -1101,7 +1101,7 @@ def test_pocket_no_redirect_adjust_url(self):
"""Pocket for mobile with no redirect"""
assert (
self._render("en-US", None, "test-page") == "https://app.adjust.com/m54twk?"
"campaign=www.mozilla.org&adgroup=test-page&mz_pr=pocket&mz_pl=mobile"
"campaign=www.mozilla.org&adgroup=test-page&mz_pr=pocket"
)


Expand Down
53 changes: 39 additions & 14 deletions docs/attribution/0001-analytics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -384,30 +384,36 @@ link element.
```


Product Download
Product Downloads
~~~~~~~~~~~~~~~~

.. Important::

Only Firefox and Pocket are currently supported. VPN support has not been added.
VPN support has not been added. Firefox, Firefox Mobile, Focus, Klar, and Pocket are currently supported.

We are using a the custom event `product_download` to track product downloads and app store referrals
for Firefox, Pocket, and VPN. We are not using the default GA4 event file_download for a combination of reasons:
it does not trigger for the Firefox file types, we would like to collect more information than is included with
the default events, and we would like to treat product downloads as goals but not all file downloads are goals.
When the user signals their intent do install one of our products we log a download event named for the product.
This intent could be: clicking an app store badge, triggering a file download, or sending themselves the link
using the send to device widget. The events are in the format [product name]_download and all function the same.
So they use the same JavaScript "TrackProductDownload". For this documentation the following custom events will be
talked about as `product_download` :

.. Note::
- `firefox_download`
- `firefox_mobile_download`
- `focus_download`
- `klar_download`
- `pocket_download`

Most apps listed in *appstores.py* are supported but you may still want to check that the URL
you are tracking is identified as valid in ```isValidDownloadURL``` and will be recognized by ```getEventFromUrl``.
We are not using the default GA4 event file_download for a combination of reasons:
it does not trigger for the Firefox file types, we would like to collect more information than is included with
the default events, and we would like to treat product downloads as goals but not all file downloads are goals.

Properties for use with `product_download` (not all products will have all options):

- product (example: firefox)
- platform (example: win64)
- method (store, site, or adjust)
- release_channel (example: nightly)
- download_language (example: en-CA)
- product (one of: firefox, firefox_mobile, focus, klar, pocket, vpn)
- platform **optional** (one of: win, win-msi, win64, win64-msi, win64-aarch64, macos, linux, linux64, android, ios)
- method (one of: site, store, or adjust)
- release_channel **optional** (one of: release, esr, devedition, beta, nightly)
- download_language **optional** (example: en-CA)

There are two ways to use TrackProductDownload:

Expand All @@ -426,6 +432,25 @@ There are two ways to use TrackProductDownload:
You do NOT need to include ``datalayer-productdownload-init.es6.js`` in the page bundle, it is already included
in the site bundle.

.. Note::

Most apps listed in *appstores.py* are supported but you may still want to check that the URL
you are tracking is identified as valid in ```isValidDownloadURL``` and will be recognized by ```getEventFromUrl``.


If you would like to track something as a download that is not currently in the *appstores.py* you can
get and send the event object manually. This most often happens with adjust links generated for specific campaigns:

.. code-block:: javascript

let customEventObject = TrackProductDownload.getEventObject(
'firefox_mobile',
'', // if you are not redirecting to a specific store, leave platform empty
'adjust'
);
TrackProductDownload.sendEvent(customEventObject);


Widget Action
~~~~~~~~~~~~~

Expand Down
32 changes: 25 additions & 7 deletions media/js/base/datalayer-productdownload.es6.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ TrackProductDownload.getEventObject = (
download_language = false
) => {
const eventObject = {};
eventObject['event'] = 'product_download';
eventObject['event'] = product + '_download';
eventObject['product'] = product;
eventObject['platform'] = platform;
eventObject['method'] = method;
Expand Down Expand Up @@ -138,15 +138,15 @@ TrackProductDownload.getEventFromUrl = (downloadURL) => {

switch (idParam) {
case 'org.mozilla.firefox':
androidProduct = 'firefox';
androidProduct = 'firefox_mobile';
androidRelease = 'release';
break;
case 'org.mozilla.fenix':
androidProduct = 'firefox';
androidProduct = 'firefox_mobile';
androidRelease = 'nightly';
break;
case 'org.mozilla.firefox_beta':
androidProduct = 'firefox';
androidProduct = 'firefox_mobile';
androidRelease = 'beta';
break;
case 'org.mozilla.focus':
Expand All @@ -169,7 +169,7 @@ TrackProductDownload.getEventFromUrl = (downloadURL) => {
} else if (appStoreURL.test(downloadURL) || iTunesURL.test(downloadURL)) {
let iosProduct = 'unrecognized';
if (downloadURL.indexOf('/id989804926') !== -1) {
iosProduct = 'firefox';
iosProduct = 'firefox_mobile';
} else if (downloadURL.indexOf('/id1055677337') !== -1) {
iosProduct = 'focus';
} else if (downloadURL.indexOf('/id1073435754') !== -1) {
Expand All @@ -185,9 +185,11 @@ TrackProductDownload.getEventFromUrl = (downloadURL) => {
'release'
);
} else if (adjustURL.test(downloadURL)) {
const adjustProduct = params.mz_pr ? params.mz_pr : 'unrecognized';
const adjustPlatform = params.mz_pl ? params.mz_pl : '';
eventObject = TrackProductDownload.getEventObject(
params.mz_pr,
params.mz_pl,
adjustProduct,
adjustPlatform,
'adjust',
'release'
);
Expand Down Expand Up @@ -234,6 +236,22 @@ TrackProductDownload.sendEventFromURL = (downloadURL) => {
*/
TrackProductDownload.sendEvent = (eventObject) => {
window.dataLayer.push(eventObject);
// we also want to keep the old event name around for a few months to help with the transition
// this can be deleted as part of the UA cleanup
TrackProductDownload.sendOldEvent(eventObject);
};

/**
* Sends an version of the old product_download event to the data layer
* @param {Object} - product details formatted into a product_download event
*/
TrackProductDownload.sendOldEvent = (eventObject) => {
// deep copy of event object
const oldEventObject = JSON.parse(JSON.stringify(eventObject));
// replace event name with old event name
oldEventObject['event'] = 'product_download';
// add to dataLayer
window.dataLayer.push(oldEventObject);
};

export default TrackProductDownload;
20 changes: 13 additions & 7 deletions tests/unit/spec/base/datalayer-productdownload.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ describe('TrackProductDownload.isValidDownloadURL', function () {
describe('TrackProductDownload.getEventObject', function () {
it('should insert parameters into the proper place in the event object', function () {
let testFullEventExpectedObject = {
event: 'product_download',
event: 'testProduct_download',
product: 'testProduct',
platform: 'testPlatform',
method: 'testMethod',
Expand All @@ -83,7 +83,7 @@ describe('TrackProductDownload.getEventObject', function () {
});
it('should create an event object even if there are no release_channel or download_language parameters', function () {
let testShortEventExpectedObject = {
event: 'product_download',
event: 'testProduct_download',
product: 'testProduct',
platform: 'testPlatform',
method: 'testMethod'
Expand All @@ -109,19 +109,19 @@ describe('TrackProductDownload.getEventFromUrl', function () {
let testEvent = TrackProductDownload.getEventFromUrl(
'https://itunes.apple.com/app/firefox-private-safe-browser/id989804926'
);
expect(testEvent['product']).toBe('firefox');
expect(testEvent['product']).toBe('firefox_mobile');
});
it('should identify product for Firefox in the Play Store', function () {
let testEvent = TrackProductDownload.getEventFromUrl(
'https://play.google.com/store/apps/details?id=org.mozilla.firefox&referrer=utm_source%3Dmozilla%26utm_medium%3DReferral%26utm_campaign%3Dmozilla-org'
);
expect(testEvent['product']).toBe('firefox');
expect(testEvent['product']).toBe('firefox_mobile');
});
it('should identify product for Firefox with Adjust', function () {
let testEvent = TrackProductDownload.getEventFromUrl(
'https://app.adjust.com/2uo1qc?mz_pr=firefox&mz_pl=mobile'
'https://app.adjust.com/2uo1qc?mz_pr=firefox_mobile&mz_pl=mobile'
);
expect(testEvent['product']).toBe('firefox');
expect(testEvent['product']).toBe('firefox_mobile');
});
it('should identify product for Pocket in the App Store', function () {
let testEvent = TrackProductDownload.getEventFromUrl(
Expand Down Expand Up @@ -244,6 +244,12 @@ describe('TrackProductDownload.getEventFromUrl', function () {
);
expect(testEvent['platform']).toBe('ios');
});
it('should not identify platform for Adjust link without redirect', function () {
let testEvent = TrackProductDownload.getEventFromUrl(
'https://app.adjust.com/b8s7qo'
);
expect(testEvent['platform']).toBe('');
});
// release channel
it('should identify release_channel for Firefox Release', function () {
let testEvent = TrackProductDownload.getEventFromUrl(
Expand Down Expand Up @@ -383,7 +389,7 @@ describe('TrackProductDownload.handleLink', function () {
expect(TrackProductDownload.isValidDownloadURL).toHaveBeenCalled();
expect(TrackProductDownload.getEventObject).toHaveBeenCalled();
expect(TrackProductDownload.sendEvent).toHaveBeenCalledWith({
event: 'product_download',
event: 'firefox_download',
product: 'firefox',
platform: 'win64',
method: 'site',
Expand Down