Google Analytics Custom Dimension not sending properly - google-analytics

I am setting up click tracking for a ticket management app. We have some custom dimensions we want to track, but it doesn't seem like some of them are coming through properly.
function fees(fee, userID) {
const analyticsObject = {
dimension1: userID, //<uuid>
dimension2: fee.ticketNumber, //<int>
dimension3: fee.selectedFee.consultantFee, //<int>
dimension4: fee.selectedFee.price, //<int>
dimension8: fee.selectedFee.name //<string>
};
sendEvent('event', 'Fee', 'click', analyticsObject);
}
All of those dimensions are set properly in Google Analytics, but for whatever reason dimension8 will not show up in any of the reports I do.
One particular reports drilldown for consultantFees is set up:
userID
- ticketNumber
- - feeName
- - - consultantFee
However, when I get to level 3, (feeName) nothing shows up, at all. I fear I'm just missing something somewhere, but this is also happening on another custom dimension.
function nxt(note, ticketNumber) {
const analyticsObject = {
dimension1: note.user._id,
dimension2: ticketNumber,
dimension7: note.body
}
sendEvent('event', 'NXT', 'click', analyticsObject);
}
This report is set up:
userID
- ticketNumber
- - noteBody
Again, the third level shows nothing.

Related

Fix Self referral issue in GA4 property

Our GA4 property in Google analytics is showing our own website as a referral source. Normaly, in ga3 property there is a filter where you can exclude sites like payment portals and such. In the new property this feature is not yet available.
We tried using the following script to workaround the problem
var ref = {{Referrer}};
// don't bother if there is no referrer
if (!ref) return ref;
var newref;
// place your external referrers here (domain names)
// adding 'foo.bar.com' matches 'www.foo.bar.com' too
var domains = [
// banks
'rabobank.nl', 'ing.nl', 'abnamro.nl', 'regiobank.nl', 'snsbank.nl',
'asnbank.nl', 'triodos.nl', 'vanlanschot.nl', 'knab.nl', 'bunq.com',
'frieslandbank.nl', 'snsreaal.nl', 'secure-ing.com',
// payment providers, cards, foreign banks
'mollie.nl', 'mollie.com', 'paypal.com', 'paypal.nl', 'adyen.com',
'multisafepay.com', 'visa.com', 'wlp-acs.com', 'belfius.be', 'payin3.nl',
'icscards.nl', 'arcot.com', 'securesuite.co.uk', 'hsbc.com.hk',
'cm-cic.com', 'pay.nl', 'redsys.es', 'tatrabanka.sk'
];
domains.forEach(function(x) {
// loop through domains,
if(ref.match(RegExp('^https?://([^.]+\.)?'+ x +'/')))
newref = x;
})
// return referrer, or the new one
return newref ?
'https://' + {{Page Hostname}} + '/excluded-referrer/' + newref
: ref
}
The script does not work though. Could you give me any new recommendation on how to solve this issue or tell me if the code might be wrong?
Thanks
Update: Google actually releases gradually a referral exclusion feature in GA4, which you can find here:https://support.google.com/analytics/answer/10327750?ck_subscriber_id=750579406
Thankfully this will help with this problem.

Enhanced ecommerce don't recognize internal promotion view object

I've been implementing all the enhanced eCommerce tracks for the past few weeks and I could do most of the job successfully thanks to Simo Ahava's blog. But now I'm struggling with the internal promotion view tracking.
I choose to implement the view tracking with the concept of True View Impressions also with a base on Simo's work and for products it was ok. So I modified the customTasks from the link to track internal promotion but, for some reason, the enhanced eCommerce isn't recognizing the promoView object. But it's recognizing the promoClick (?).
I've made a test: I substitute the promoClick for a impression object and it works! So, my strong guest, it's that the problem it's really on my object. My object's format can be seen here.
And to illustrate the way the object it's being constructed:
var targetElement = {{Click Element}},
event = {{Event}},
batch = window[promoBatchVariableName],
impressions = google_tag_manager[{{Container ID}}].dataLayer.get('ecommerce.promoView.promotions'),
ecomObj = { };
if (event === 'gtm.click') {
while (!targetElement.getAttribute(promoIdAttribute) && targetElement.tagName !== 'BODY') {
targetElement = targetElement.parentElement;
}
}
var latestPromoImpression = impressions.filter(function(impression) {
return impression.id === targetElement.getAttribute(promoIdAttribute);
}).shift();
var promoImpressionsArr = batch.map(function(id) {
return impressions.filter(function(impression) {
return impression.id === id;
}).shift();
});
if (event === 'gtm.elementVisibility'){
promoImpressionsArr[maxPromoBatch - 1] = latestPromoImpression;
}
console.log(ecomObj)
ecomObj.promoView = { promotions: promoImpressionsArr};
if (event === 'gtm.click') {
ecomObj.promoClick = {
promotions: [latestPromoImpression]
};
console.log("click")
}
return {
ecommerce: ecomObj
};
}
Could someone help me with some ideas?
This answer is just to close the question! As I pointed in the comments:
" I found the problem. And it's not on my object itself only. xD The problem is the undefined elements as you pointed at the beginning of our talk. I'm waiting for the dev team to change the data-attributes of the elements on our site's pages because sometimes we don't get any individual identifier variable. So, in the meantime, I've implemented a way to get always a product id even in these cases but as the identifier doesn't exist in the CSS selector if the element has an id in the 'entrance object', the element is set as undefined. "

I am noticing double entry (cpc and organic) for the same user ? little confused

I'm noticing double entry in google analytics. I have multiple ocurrences where it looks like the user came from the CPC campaign (which always has a 0s session duration) but that very same user also has an entry for "organic" and all the activities are logged under that.
My site is not ranked organically for those keywords. Unless a so many users come to my site, leave, and google for my "brand name" on google and revisits, this doesn't make sense.
I'm a little confused. Here's the report:
preview from google analytics dashboard
Based on the additional information in your comment, that the sites is a Single Page Application (SPA), you are most likely facing the problem of 'Rogue Referral'.
If this is the case, what happens, is that you overwrite the location field in the Analytics hit, losing the original UTM parameters, whereas referral is still sent with the hit, so Analytics recognizes the second hit as a new traffic source. One of the solutions is to store the original page URL and send it as the location, while sending the actual visited URL in the page field.
A very good article on this topic with further tips, by Simo Ahava, is available for your help.
Also please note, that as you have mentioned, that the first hit shows 0 second time on page, you might need to check, whether the first visited page is sent twice. E.g. sending a hit on the traditional page load event, and sending a hit for the same page as a virtual page view.
I have come up with a solution to this problem in a Gatsby website (a SPA), by writing the main logic in the gatsby-browser.js file, inside the onRouteUpdate function.
You can use this solution in other contexts, but please note that the code needs to run at the first load of the page and at every route change.
If you want the solution to work in browsers that do not support URLSearchParams I think you can easily find a polyfill.
Function to retrieve the parameters
// return the whole parameters only if at least one of the desired parameters exists
const retrieveParams = () => {
let storedParams;
if ('URLSearchParams' in window) {
// Browser supports URLSearchParams
const url = new URL(window.location.href);
const params = new URLSearchParams(url.search);
const requestedParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'gclid'];
const hasRequestedParams = requestedParams.some((param) => {
// true if it exists
return !!params.get(param);
});
if (hasRequestedParams) {
storedParams = params;
}
}
return storedParams;
}
Create the full URL
// look at existing parameters (from previous page navigations) or retrieve new ones
const storedParams = window.storedParams || retrieveParams();
let storedParamsUrl;
if (storedParams) {
// update window value
window.storedParams = storedParams;
// create the url
const urlWithoutParams = document.location.protocol + '//' + document.location.hostname + document.location.pathname;
storedParamsUrl = `${urlWithoutParams}?${storedParams}`;
}
Send the value to analytics (using gtag)
// gtag
gtag('config', 'YOUR_GA_ID', {
// ... other parameters
page_location: storedParamsUrl ?? window.location.href
});
or
gtag('event', 'page_view', {
// ... other parameters
page_location: storedParamsUrl ?? window.location.href,
send_to: 'YOUR_GA_ID'
})

Saving google sheets data into Firebase using Google Apps script

I'm trying to have my google sheets synced with my firebase database. I'm not very experienced with javaScript, so is it possible using the below method? The idea is that it would automatically sync every time a new row gets created/updated/deleted. I know that I need the script files but not sure how to import them in the .gs file, so that's why it's in the html.
Many thanks!
translate.gs
function saveToFirebase() {
var config = {
apiKey: "MY_API_KEY",
authDomain: "MY_DOMAIN.firebaseapp.com",
databaseURL: "MY_DOMAIN.firebaseio.com",
projectId: "MY_DOMAIN",
storageBucket: "MY_DOMAIN.appspot.com",
messagingSenderId: "MESSAGE_ID"
};
firebase.initializeApp(config);
var database = firebase.database();
database.ref('food/' + MY_USER_UID).set({
name: "pizza funghi",
});
}
sidebar.html
<!DOCTYPE html>
<html>
<head>
<script src="https://www.gstatic.com/firebasejs/4.12.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.12.0/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.12.0/firebase-database.js"></script>
</head>
<body>
</body>
</html>
There is a third-party libarary which integrates with Firebase's REST API. If you're comfortable using it, this becomes pretty straightforward.
First we'll need to create a tab to track changes. We need the identity of those who make changes, so we have to break this into two parts - a simple onEdit trigger which runs as the modifying user, and an installable trigger which I'll call uploadChanges. The latter is what talks to Firebase.
Create a tab called changes
Add a frozen row with the following headers:
Uploaded
User
Value
Install the third party Firebase library
Begin by clicking Resources > Libraries in the script editor, then pasting MYeP8ZEEt1ylVDxS7uyg9plDOcoke7-2l in the "Find a Library" box. Hit Save.
Opt for stability by choosing the latest public release, or choose the latest release (I chose latest while writing this).
Click OK
Now would be a good time to peruse the reference docs so you know what I'm up to in the below instructions :-)
Set up security (I'm assuming you want this script to run as you)
Make your Google account (which runs the script) be at least an Editor for your Firebase project.
Set the appropriate authorization scopes for your App Script project:
Go to File > Project Properties > Scopes in the App Script editor
Select View > Show manifest file (the manifest file is usually hidden by default)
Add https://www.googleapis.com/auth/userinfo.email and https://www.googleapis.com/auth/firebase.database to the oauthScopes array (add it if it's not already there)
Save the manifest file. Next time you run the script you'll get a pop-up asking about permissions.
The equivalent of your translate.gs above, which always just sets your food to 'pizza funghi`, would look like this:
function saveToFirebase() {
var dbUrl = "MY_DOMAIN.firebaseapp.com"; // Set appropriately
var token = ScriptApp.getOAuthToken(); // Depends on security setup above
var firebase = FirebaseApp.getDatabaseByUrl(dbUrl, token);
newData = {
name: "pizza funghi",
};
firebase.setData('food/' + MY_USER_UID, newData);
}
But you said you wanted to update Firebase on every save. To do this you really just want to rip off one of the various onEdit tutorials floating around the net. The resulting onEdit should look something like this:
function onEdit(e) {
// First get stuff about the edit.
// This approach only gets the top left cell of a multi-cell edit.
var editRange = e.range; // The edited range
var newValue = editRange.getValue();
// Next, who is the editor? Remove the `split` for full email.
var username = Session.getActiveUser().getEmail().split('#')[0];
if (username == '') {
username = SOME_REASONABLE_DEFAULT; // Or give up if you wish
}
// Finally save the change
SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName('changes')
.appendRow([false, username, newValue]);
}
function uploadChanges() {
// Attach to Firebase
var dbUrl = "MY_DOMAIN.firebaseapp.com"; // Set appropriately
var token = ScriptApp.getOAuthToken(); // Depends on security setup above
var firebase = FirebaseApp.getDatabaseByUrl(dbUrl, token);
// Get content of changes tab
var changeSheet = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName('changes');
var changeData = changeSheet.getDataRange()
.getValues();
// Upload all new-to-us changes
for (var i = 1; i < changeData.length; i++) {
if (changeData[i][0]) {
continue; // We already uploaded this one
}
changeData[i][0] = true; // Optimistically assume we'll succeed
var newData = {
name: changeData[i][2]
};
var username = changeData[i][1];
firebase.setData('food/' + username, newData);
}
// Blanket update of change-data sheet to update upload status
changeSheet.getRange(1, 1, changeData.length, changeData[0].length)
.setValues(changeData);
}
Lastly, set up some triggers.
Choose Edit > Current Project's Triggers in the script editor
Add a new trigger for onEdit
Choose onEdit from the leftmost Run dropdown
Choose From spreadsheet in the Events dropdown
Then choose On edit in the rightmost dropdown
Add a new trigger for uploadChanges
Choose uploadChanges from the leftmost Run dropdown
Choose Time-driven from the Run dropdown
Set up a schedule that's appropriate to your needs
EDIT: My original script had you doing everything in onEdit, which tehhowch correctly points out won't work since we're talking to another service. I've updated to stage to a "changes" tab which I include in setup. My new approach maintains a perpetual record of old uploads; for performance you might instead choose to just clear the changes sheet once you've done the upload.

Analytics Scripts Insufficient Permission from Google Sheets

I'm trying to write a script to grab Google Analytics data & add it to a Google Sheet.
When running the following code, I get the following error on the sheet:
"User does not have sufficient permissions for this profile."
Just a few quick check-box items:
Yes, I have admin permissions for the Analytics account I'm trying to access
Yes, I have admin permissions for the Google Sheet from which I'm creating the script
Yes, I've double-checked my current Google login to make sure I'm on the right account.
Here is the code:
function runDemo() {
try {
var results = getReportDataForProfile();
outputToSpreadsheet(results);
} catch(error) {
Browser.msgBox(error.message);
}
}
function getReportDataForProfile() {
var profileId = 'xxxxxxxx'; //firstProfile.getId();
var tableId = 'ga:' + profileId;
var startDate = getLastNdays(14); // 2 weeks (a fortnight) ago.
var endDate = getLastNdays(0); // Today.
var optArgs = {
'dimensions': 'ga:keyword', // Comma separated list of dimensions.
'sort': '-ga:sessions,ga:keyword', // Sort by sessions descending, then keyword.
'segment': 'dynamic::ga:isMobile==Yes', // Process only mobile traffic.
'filters': 'ga:source==google', // Display only google traffic.
'start-index': '1',
'max-results': '250' // Display the first 250 results.
};
// Make a request to the API.
var results = Analytics.Data.Ga.get(
tableId, // Table id (format ga:xxxxxx).
startDate, // Start-date (format yyyy-MM-dd).
endDate, // End-date (format yyyy-MM-dd).
'ga:sessions,ga:pageviews', // Comma seperated list of metrics.
optArgs);
if (results.getRows()) {
return results;
} else {
throw new Error('No views (profiles) found');
}
}
OK, I screwed around with this for a while and then it started working.
My best guess is I copied the property ID from Google Analytics wrong. After going back and recopying it, everything worked well.

Resources