├── Code.gs └── README.md /Code.gs: -------------------------------------------------------------------------------- 1 | function getAccountSummary() { 2 | // Get account summary to build GA hierarchy 3 | return Analytics.Management.AccountSummaries.list({ 4 | fields: 'items(id,name,webProperties(id, name, profiles(id, name)))' 5 | }); 6 | } 7 | 8 | function getSheet(name) { 9 | var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(name); 10 | var ui = SpreadsheetApp.getUi(); 11 | var response; 12 | if (sheet) { 13 | response = ui.alert('Sheet named ' + name + ' already exists! Click OK to overwrite, CANCEL to abort.', ui.ButtonSet.OK_CANCEL); 14 | return response === ui.Button.OK ? sheet : false; 15 | } 16 | return SpreadsheetApp.getActiveSpreadsheet().insertSheet(name); 17 | } 18 | 19 | function writeGaHierarchy() { 20 | var sheet = getSheet('GA Hierarchy'), 21 | headers = ['Account ID', 'Account Name', 'Property ID', 'Property Name', 'View ID', 'View Name', 'Select for analysis (x/X)'], 22 | items = getAccountSummary().getItems(), 23 | final = []; 24 | var i, j, k; 25 | 26 | // Abort if sheet existed and user pressed CANCEL 27 | if (sheet === false) { 28 | return; 29 | } 30 | 31 | // Clear the GA Hierarchy sheet 32 | sheet.clear(); 33 | // Build hierarchy of accounts, properties, and views 34 | if (items) { 35 | sheet.getRange(1, 1, 1, headers.length).setValues([headers]); 36 | for (i = 0; i < items.length; i++) { 37 | if (items[i].webProperties) { 38 | for (j = 0; j < items[i].webProperties.length; j++) { 39 | if (items[i].webProperties[j].profiles) { 40 | for (k = 0; k < items[i].webProperties[j].profiles.length; k++) { 41 | final.push([ 42 | items[i].id, 43 | items[i].name, 44 | items[i].webProperties[j].id, 45 | items[i].webProperties[j].name, 46 | items[i].webProperties[j].profiles[k].id, 47 | items[i].webProperties[j].profiles[k].name 48 | ]); 49 | } 50 | } 51 | } 52 | } 53 | } 54 | sheet.getRange(2, 1, final.length, headers.length - 1).setNumberFormat('@').setValues(final); 55 | } 56 | } 57 | 58 | function isEmpty(sheet) { 59 | // Check if sheet is empty after the header row 60 | var firstItem = sheet.getRange(2, 1).getValues(); 61 | return firstItem[0][0] === ''; 62 | } 63 | 64 | function getCustomDims(accountId, propertyId) { 65 | // Get list of Custom Dimensions for given account/property 66 | return Analytics.Management.CustomDimensions.list(accountId, propertyId); 67 | } 68 | 69 | function getHitData(dimId, profileId) { 70 | // Get last 7 days hit data for given Custom Dimension 71 | return Analytics.Data.Ga.get('ga:' + profileId, '7daysAgo', 'today', 'ga:hits', {dimensions: dimId}); 72 | } 73 | 74 | function buildGaData(sourceDataValues) { 75 | // Build the list of Custom Dimensions for selected items 76 | var data = {'lastItem': 0}; 77 | var idx, dim, customDims, collectedHits, item, accountName, accountId, propertyId, profileId, propertyName, profileName; 78 | for (idx = 0; idx < sourceDataValues.length; idx++) { 79 | accountId = sourceDataValues[idx][0]; 80 | accountName = sourceDataValues[idx][1]; 81 | propertyId = sourceDataValues[idx][2]; 82 | propertyName = sourceDataValues[idx][3]; 83 | profileId = sourceDataValues[idx][4]; 84 | profileName = sourceDataValues[idx][5]; 85 | // Show progress popup 86 | SpreadsheetApp.getActiveSpreadsheet().toast(propertyId + " " + profileName, "Processing " + (idx + 1) + "/" + sourceDataValues.length); 87 | // Fetch custom dimensions for given property 88 | customDims = getCustomDims(accountId, propertyId); 89 | // Build the data structure for each given account/property/profile/dimension 90 | data['item' + idx] = []; 91 | data['item' + idx].push([accountName, accountName, accountName, accountName]); 92 | data['item' + idx].push([propertyName, propertyName, propertyName, propertyName]); 93 | data['item' + idx].push([propertyId, propertyId, propertyId, propertyId]); 94 | data['item' + idx].push([profileName, profileName, profileName, profileName]); 95 | data['item' + idx].push([profileId, profileId, profileId, profileId]); 96 | data['item' + idx].push(['NAME', 'SCOPE', 'ACTIVE', 'LAST 7 DAYS']); 97 | // For each Custom dim 1-200, fetch the name, scope, and activity status, or enter blank string if not available 98 | for (dim = 0; dim < 200; dim++) { 99 | item = customDims.items[dim]; 100 | if (item) { 101 | data['item' + idx].push([customDims.items[dim].name, customDims.items[dim].scope, customDims.items[dim].active, '']); 102 | } else { 103 | data['item' + idx].push(['', '', '', '']); 104 | } 105 | } 106 | data['lastItem'] = idx + 1; 107 | } 108 | return data; 109 | } 110 | 111 | function getRowsForAnalysis(sheet) { 112 | // Return all the rows that have been "selected" from the GA Hierarchy 113 | var data = [], 114 | range = sheet.getRange(2, 1, sheet.getLastRow() - 1, 7).getValues(); 115 | var i; 116 | for (i = 0; i < range.length; i++) { 117 | if (range[i][6] === 'x' || range[i][6] === 'X') { 118 | data.push(range[i]); 119 | } 120 | } 121 | return data; 122 | } 123 | 124 | function buildGaSheet(sheet) { 125 | // Build the GA Dimensions sheet 126 | var sourceData = getRowsForAnalysis(sheet), 127 | gaSheet = getSheet('GA Dimensions'), 128 | firstCol = [['Account name'], ['Property name'], ['Property ID'], ['View name'], ['View ID'], ['Dimension']]; 129 | var i, columnData; 130 | 131 | // Abort if sheet existed and user clicked CANCEL 132 | if (gaSheet === false) { 133 | return; 134 | } 135 | 136 | if (sourceData.length === 0) { 137 | throw new Error('No rows selected for analysis.'); 138 | } 139 | // Clear the GA Dimensions sheet 140 | gaSheet.clear(); 141 | // Add the first column with identifiers and dimension IDs 142 | for (i = 1; i <= 200; i++) { 143 | firstCol.push(['ga:dimension' + i]); 144 | } 145 | gaSheet.getRange(1, 1, 206, 1).setValues(firstCol); 146 | // Fetch the source data based on selected rows 147 | columnData = buildGaData(sourceData); 148 | // For each item in the source data, add a set of columns with the dimension data 149 | for (i = 0; i < columnData['lastItem']; i++) { 150 | gaSheet.getRange(1, 2 + (i * 4), 206, 4).setNumberFormat('@').setValues(columnData['item' + i]); 151 | } 152 | // Freeze the first column to make it easier to navitate 153 | gaSheet.setFrozenColumns(1) 154 | }; 155 | 156 | function runValidator() { 157 | var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('GA Hierarchy'); 158 | if (!sheet) { 159 | throw new Error('You must first create the Google Analytics Hierarchy'); 160 | } 161 | if (!isEmpty(sheet)) { 162 | buildGaSheet(sheet); 163 | } 164 | } 165 | 166 | function getGaHits() { 167 | // Fetch last 7 days data for selected UA-12345-1 property 168 | var sheet = SpreadsheetApp.getActiveSheet(), 169 | last7Days = false, 170 | dimensions = {}; 171 | var i, profileId, selectedCell, selectedColumn, dimensionRange; 172 | if (sheet.getName() !== 'GA Dimensions') { 173 | throw new Error('You must be in the GA Dimensions sheet'); 174 | } 175 | selectedCell = sheet.getActiveCell(); 176 | selectedColumn = selectedCell.getColumn(); 177 | if (selectedColumn === 1 || !/^UA-/.test(sheet.getRange(3, selectedColumn, 1, 1).getValue())) { 178 | throw new Error('You must select one of the populated data columns!'); 179 | } 180 | // Allow the user to select any one of the four UA-12345-1 items in the GA Dimensions list 181 | switch (sheet.getRange(6, selectedColumn).getValue()) { 182 | case 'NAME': 183 | selectedColumn += 3; 184 | break; 185 | case 'SCOPE': 186 | selectedColumn += 2; 187 | break; 188 | case 'ACTIVE': 189 | selectedColumn += 1; 190 | break; 191 | } 192 | profileId = sheet.getRange(5, selectedColumn).getValue(); 193 | for (i = 1; i <= 200; i++) { 194 | dimensions['ga:dimension' + i] = ''; 195 | } 196 | // Only fetch hit data for dimensions that have active === 'true' 197 | dimensionRange = sheet.getRange(7, selectedColumn - 1, 200, 1).getValues(); 198 | for (i = 0; i < dimensionRange.length; i++) { 199 | if (dimensionRange[i][0] === 'true') { 200 | dimensions['ga:dimension' + (i + 1)] = getHitData('ga:dimension' + (i + 1), profileId)['totalsForAllResults']['ga:hits']; 201 | } 202 | } 203 | for (i = 1; i <= 200; i++) { 204 | sheet.getRange(7 + (i - 1), selectedColumn, 1, 1).setValue(dimensions['ga:dimension' + i]); 205 | } 206 | } 207 | 208 | function onOpen(e) { 209 | var menu = SpreadsheetApp.getUi().createAddonMenu(); 210 | menu.addItem('1. Build Google Analytics hierarchy', 'writeGaHierarchy'); 211 | menu.addItem('2. Run validator', 'runValidator'); 212 | menu.addItem('3. Fetch last 7 days data for selected view', 'getGaHits'); 213 | menu.addToUi(); 214 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Google Analytics Validator (Apps Script) 2 | You can find this tool in the Chrome Web Store, where you can add it to Google Sheets. 3 | 4 | https://chrome.google.com/webstore/detail/ga-gtm-validator/nmjiiaaejkhpegmcpfaehmbijgoilimo 5 | --------------------------------------------------------------------------------