I am using this script to pull GA4 data into a google sheet via the Google Analytics Data API. Here is the code:
function runReport() {
const propertyId = '111222333';
try {
const metric = AnalyticsData.newMetric();
metric.name = 'Event count';
metric.name = 'Total users';
metric.name = 'Sessions';
const dimension = AnalyticsData.newDimension();
dimension.name = 'Event name';
dimension.name = 'Link URL';
dimension.name = 'Date';
dimension.name = 'Stream name';
const dateRange = AnalyticsData.newDateRange();
dateRange.startDate = '2022-05-01';
dateRange.endDate = 'today';
const request = AnalyticsData.newRunReportRequest();
request.dimensions = [dimension];
request.metrics = [metric];
request.dateRanges = dateRange;
const report = AnalyticsData.Properties.runReport(request,
'properties/' + propertyId);
if (!report.rows) {
Logger.log('No rows returned.');
return;
}
Edit: I'd like to create a script to generate an output like this:
The multiple properties are:
111222333
222333444
444555666
777888999
Is it possible to adjust this script to pull data from multiple GA4 properties and output into one dataset?
Thanks
Related
I am trying to have my function use three sheets within its embedded spreadsheet: two fixed sheets and one active/open sheet. I need it to read the sheet I have open because that is the sheet I am changing week to week, but it is automatically defaulting to using the first sheet rather than the sheet I have opened. I altered this function from an existing function I have that works, and on this new one I only changed the message and its assigned variables. I really know absolutely nothing about coding but have been learning so I can create a custom message from a code a previous coworker wrote. I appreciate all of the help I can get x10000
function createMessage(address, dirtrider, day, window, outby, phone) {
{ var message = 'Hello ' + address + ', welcome to IVCC\'s composting program! You\'ll be receiving weekly automated reports from this number (a weekly reminder to put your bucket out and a notification if any incorrect items were discarded into your bucket). To stop receiving these messages, reply STOP. We are currently restructuring our biking routes, therefore you may be receieving a new Dirtrider according to this message. Starting next week, you\'re assigned Dirtrider will be ' + dirtrider + ', and you\'re new bucket will arrive weekly on ' + day + '\'s sometime between ' + window + '. Please have your bucket outside your front door by ' + outby + ' on this day weekly unless notified of a change. This is an automated messaging service, so please reach out to your Dirtrider directly at ' + phone + ' with any questions or concerns about your service. Thanks for composting with us! -IVCC Team';
}
return message;
}
// function to verify numbers and messages before sending //
function PrintMessages() {
var mainsheet = SpreadsheetApp.getActiveSheet();
var data = mainsheet.getDataRange().getValues();
var contactsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Contact Sheet");
var contacts = contactsheet.getDataRange().getValues();
var messageLog_sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Message Log");
var messageLog = messageLog_sheet.getDataRange().getValues;
var mLrow = 2 //row to start at on Message Log spreadsheet
// runs through every row of active spreadsheet, i = row //
for (var i = 2; i < data.length; i++) {
// set variables based on values in sheet and create message//
var address = data[i][0];
var day = data[i][1];
var window = data[i][2];
var outby = data[i][3];
var dirtrider = data[i][4]
var phone = data[i][5]
var message = createMessage(address, dirtrider, day, window, outby, phone);
// reference seperate contact sheet, j = column //
var nresidents = contacts[i][2];
for (var j = 1; j <= nresidents; j++) {
// log address, phone number, and message function //
var address = contacts[i][0];
var number = contacts[i][j+2];
var messageArray = [[address, number, message]];
var range = messageLog_sheet.getRange(mLrow, 1, 1, 3); //ENTER COMMENTS
range.setValues(messageArray);
var mLrow = mLrow + 1;
}
}
}
I have a Google sheet that is in a “Form” format. I need to program a button that once the sender completes the form, will send the data to another sheet in a spreadsheet format and erase the data from the “form” making it ready for another form entry.
enable goole sheets api service, and adapt this script (names of source and destination sheets, and cellsA1Notation of data in source sheet).
function onOpen() {
SpreadsheetApp.getUi().createMenu('⇩ M E N U ⇩')
.addItem('👉 Validate Unit Registration (FORM)', 'copyDataRegistrationForm')
.addItem('👉 Reset Unit Registration (FORM)', 'initRegistrationForm')
.addToUi();
}
function copyDataRegistrationForm() {
const ssId = SpreadsheetApp.getActiveSpreadsheet().getId();
const srcSheet = "Unit Registration (FORM)";
const dstSheet = "Master Data";
const rngA1Notation = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(dstSheet).getRange(1, 1, 1, 74).getValues().flat()
const src = rngA1Notation.map(e => `'${srcSheet}'!${e}`);
const values = Sheets.Spreadsheets.Values.batchGet(ssId, { ranges: src })
var data = []
values.valueRanges.forEach(e => data.push(e.values ? e.values.flat().toString() : ""))
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(dstSheet).appendRow(data)
}
function initRegistrationForm() {
const srcSheet = "Unit Registration (FORM)";
const dstSheet = "Master Data";
const rngA1Notation = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(dstSheet).getRange(1, 1, 1, 74).getValues().flat()
var rng = rngA1Notation.filter(r => SpreadsheetApp.getActiveSpreadsheet().getRange(r).getFormula() == '')
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(srcSheet).getRangeList(rng).clearContent()
}
I have a Google Sheet which hides some sheets from most users, but when an admin user is identified, has a menu option to show all the sheets. It calls my function showAllSheets, as follows:
function showAllSheets() {
var sheets=SpreadsheetApp.getActiveSpreadsheet().getSheets();
for(var i = 0; i < sheets.length; i++){
//Show each sheet
sheets[i].showSheet();
console.log('Showing Sheet: ' + sheets[i].getName())
}
}
But this function doesn't always work. I get very mixed results. Sometimes it shows all sheets as expected. Sometimes it shows some of the sheets. Sometimes it eventually shows all or some of the sheets but only after a long delay (1 minute+) and sometimes, it does nothing at all.
I'm checking my execution time in the "Executions" section of Apps Script. This function typically executes in about 2-3 seconds and the console log contains all expected messages. It will say "completed" and still my sheets aren't showing. Sometimes it will eventually show the sheets some time after it says execution is complete and again, sometimes they never show.
I have an onOpen installable trigger and an onSelectionChange simple trigger, so at first I was concerned maybe my scripts are running into each other. However, I've confirmed I still have this issue even if I make sure all other scripts have completed before I run it.
I have no issues with .hideSheet() in my functions that hide the sheets (one being RestoreDefaultView, the other in my onOpen trigger.). They always get hidden immediately.
Here is the menu code I'm using:
var ui = SpreadsheetApp.getUi();
if (thisuser.group == 'admin') {
ui.createMenu('MyProject(Admin)')
.addSubMenu(
ui.createMenu('View')
.addItem('Restore Default Sheet View', 'restoreDefaultView')
.addItem('Show All Sheets', 'showAllSheets')
)
.addToUi();
}
What is going on and what can I do to fix it?
EDIT: Per request, here is the function that is hiding some of the sheets:
function restoreDefaultView() {
const name_Master = 'PROJECT MASTER';
const name_Lists = 'LISTS';
const name_Guide = 'GUIDE';
const name_Access = 'ACCESS';
const name_ActionForm = 'frm_Action';
const name_ProjectForm = 'frm_Project';
const name_NotesForm = 'frm_Notes';
const name_AdminForm = 'frm_Admin';
const name_GroupAdmin = 'admin_Groups';
//const id_ProjectList = '(redacted)'; // Google Drive file ID of Project List'
//const id_TaskList = ''; //Google Drive file ID of Task List
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sht_Master = ss.getSheetByName(name_Master);
var sht_Guide = ss.getSheetByName(name_Guide);
var sht_Lists = ss.getSheetByName(name_Lists);
var sht_Access = ss.getSheetByName(name_Access);
var sht_ActionForm = ss.getSheetByName(name_ActionForm);
var sht_ProjectForm = ss.getSheetByName(name_ProjectForm);
var sht_NotesForm = ss.getSheetByName(name_NotesForm);
var sht_AdminForm = ss.getSheetByName(name_AdminForm);
var sht_GroupAdmin = ss.getSheetByName(name_GroupAdmin);
//Sheets Normally Displayed
//Immediately activate Master sheet after making visible to minimize confusion
sht_Master.showSheet();
sht_Master.activate();
sht_Guide.showSheet();
//Sheets Normally Hidden
sht_Lists.hideSheet();
sht_Access.hideSheet();
sht_ActionForm.hideSheet();
sht_ProjectForm.hideSheet();
sht_NotesForm.hideSheet();
sht_AdminForm.hideSheet();
sht_GroupAdmin.hideSheet();
}
Try this:
function showAllSheets() {
var sheets=SpreadsheetApp.getActiveSpreadsheet().getSheets();
for(var i = 0; i < sheets.length; i++){
sheets[i].showSheet();
SpreadsheetApp.flush();
console.log('Showing Sheet: ' + sheets[i].getName())
}
}
I have a master spreadsheet with several drivers and their routes for the day. This needs to be parsed out to several driver sheets. I have one sheet with all the data in the master and another with all the drivers names and spreadsheet ids to run a loop through for all drivers listed. Just runs...no action. Last three comments need to be fixed as well as it errors here and does not like .openById.
function setRoutes(){
var ScheduleSheetURL = "https://docs.google.com/spreadsheets/...";
var ActiveSpreadSheet = SpreadsheetApp.openByUrl(ScheduleSheetURL);
var AllRoutesSheetName = "RoutesIn";
var ActiveSheet = ActiveSpreadSheet.setActiveSheet(ActiveSpreadSheet.getSheetByName(AllRoutesSheetName));
var AllRouteData = ActiveSheet.getRange('A:AZ').getValues();
ActiveSheet = SpreadsheetApp.setActiveSheet(ActiveSpreadSheet.getSheetByName("Drivers"));
//var ActiveDriverCounter = ActiveSheet.getRange("A1:A").getValues();
//var DriverCount = ActiveDriverCounter.filter(String).length;
var DriverCount = ActiveSheet.getLastRow()-1;
var idrvSheetID = "";
var ActiveDriverName = "";
var RouteData = [];
var i = 1;
for (i = 1; i++; i <= DriverCount){
//Get Route Name
ActiveSpreadSheet = SpreadsheetApp.openByUrl(ScheduleSheetURL);
ActiveSheet = ActiveSpreadSheet.setActiveSheet(ActiveSpreadSheet.getSheetByName("Drivers"));
ActiveDriverName = ActiveSheet.getRange(i,1).getValue();
Logger.log(ActiveDriverName);
//Get Drivers Route
ActiveSheet = ActiveSpreadSheet.setActiveSheet(ActiveSpreadSheet.getSheetByName(AllRoutesSheetName));
for (var i = 0; i< AllRouteData.length; i++){
if(AllRouteData[i][1] == ActiveDriverName){
RouteData.push(AllRouteData[i])
Logger.log(RouteData[i]);
}
}
ActiveSpreadSheet = SpreadsheetApp.openByUrl(ScheduleSheetURL);
//Open & Write Driver Sheet
ActiveSheet = SpreadsheetApp.setActiveSheet(ActiveSpreadSheet.getSheetByName("Drivers"));
idrvSheetID = ActiveSheet.getRange(i,2).getValue();
// ActiveSpreadSheet = SpreadsheetApp.openById(idrvSheetID);
// ActiveSheet = ActiveSpreadSheet.setActiveSheet(ActiveSpreadSheet.getSheets()[0]);
// ActiveSheet.getRange(ActiveSheet.getLastRow()+1,1,RouteData.length,RouteData[0].length).setValues(RouteData);
}
}
Issues:
There are several problems with your script:
Every time you want to work on a spreadsheet or sheet, you are using some variant of setActive. I guess you are under the assumption that you need to "activate" a sheet in order to work on it, but that's not true! If you remove all this, your code can be minimized considerably.
You are not defining the outer for loop correctly: for (i = 1; i++; i <= DriverCount){. Second parameter should be the condition, and third one should be what to do at the end of each loop iteration. You have it the other way around.
When trying to filter the routes according to the Driver (if(AllRouteData[i][1] == ActiveDriverName){), you are not specifying the correct array index (arrays are 0-indexed, so 1 here would refer to column B, which doesn't contain the driver's names, so the script won't find any match there.
You are reusing the same loop counter variable (i) in a nested loop (for (var i = 0; i< AllRouteData.length; i++){). This will mess with your outer loop.
Solution:
Considering all these, I've wrote a small script that accomplishes your purpose. I have changed some of the variables names so that they are more meaningful (please check inline comments):
function setRoutes() {
const ScheduleSheetURL = "YOUR-MAIN-SPREADSHEET-URL"; // Change to your spreadsheet URL
const SourceSpreadSheet = SpreadsheetApp.openByUrl(ScheduleSheetURL);
const RoutesSheet = SourceSpreadSheet.getSheetByName("RoutesIn");
const AllRouteData = RoutesSheet.getRange('A:AZ').getValues();
const DriversSheet = SourceSpreadSheet.getSheetByName("Drivers");
const DriverCount = DriversSheet.getLastRow()-1;
const DriversData = DriversSheet.getRange(2, 1, DriverCount, 2).getValues(); // Get all Drivers data
for (let i = 0; i < DriversData.length; i++) { // Loop through all Drivers data
const DriverData = DriversData[i]; // Specific Driver data
const DriverName = DriverData[0]; // Driver name (column A)
const DriverSpreadsheetId = DriverData[1]; // Driver spreadsheet ID (column B)
const DriverRoutes = AllRouteData.filter(routeData => routeData[0] === DriverName); //Filter routes according to driver
const DriverSpreadsheet = SpreadsheetApp.openById(DriverSpreadsheetId); // Get specific Driver spreadsheet
const DriverSheet = DriverSpreadsheet.getSheets()[0]; // Get first sheet in Driver spreadsheet
DriverSheet.getRange(DriverSheet.getLastRow() + 1, 1, DriverRoutes.length, DriverRoutes[0].length)
.setValues(DriverRoutes); // Add routes to driver spreadsheet
}
}
I tried coding like spreadsheet API batch copy https://developers.google.com/google-apps/spreadsheets/#updating_multiple_cells_with_a_batch_request, The sample is base on same spreadsheet, I added a target cell but always get same error
com.google.gdata.client.batch.BatchInterruptedException: Batch Interrupted (some operations might have succeeded) : a response has already been sent for batch operation update id='R1C1'
My code like this
SpreadsheetService spreadsheetService = getSpreadsheetService(currentEmail);
WorksheetFeed feed = spreadsheetService.getFeed(getWorksheetFeedURL(sourceId), WorksheetFeed.class);
SpreadsheetEntry targetFeed = spreadsheetService.getEntry(getSpreadsheetFeedURL(targetId), SpreadsheetEntry.class);
SpreadsheetEntry sourceFeed = spreadsheetService.getEntry(getSpreadsheetFeedURL(sourceId), SpreadsheetEntry.class);
for(WorksheetEntry entry:feed.getEntries()){
WorksheetEntry targetWorksheet = spreadsheetService.insert(targetFeed.getWorksheetFeedUrl(), entry);
FeedURLFactory urlFactory = FeedURLFactory.getDefault();
URL cellFeedUrl = urlFactory.getCellFeedUrl(sourceFeed.getKey(), "od6", "private", "full");
URL targetFeedUrl = urlFactory.getCellFeedUrl(targetFeed.getKey(), "od6", "private", "full");
CellFeed cellFeed = spreadsheetService.getFeed(targetFeedUrl, CellFeed.class);
List<CellAddress> cellAddrs = new ArrayList<CellAddress>();
for (int row = 1; row <= entry.getRowCount(); ++row) {
for (int col = 1; col <= entry.getColCount(); ++col) {
cellAddrs.add(new CellAddress(row, col));
}
}
Map<String, CellEntry> cellEntries = getCellEntryMap(spreadsheetService, cellFeedUrl, cellAddrs);
CellFeed batchRequest = new CellFeed();
for (CellAddress cellAddr : cellAddrs) {
URL entryUrl = new URL(targetFeedUrl.toString() + "/" + cellAddr.idString);
CellEntry batchEntry = new CellEntry(cellAddr.row, cellAddr.col, cellAddr.idString);
String inputValue = cellEntries.get(cellAddr.idString).getCell().getInputValue();
batchEntry.changeInputValueLocal(inputValue);
batchEntry.setId(String.format("%s/%s", targetFeedUrl.toString(), cellAddr.idString));
System.out.println(targetFeedUrl.toString()+": "+cellAddr.idString+" "+ inputValue);
BatchUtils.setBatchId(batchEntry, cellAddr.idString);
BatchUtils.setBatchOperationType(batchEntry, BatchOperationType.UPDATE);
batchRequest.getEntries().add(batchEntry);
}
spreadsheetService.setHeader("If-Match", "*");
// Submit the update
Link batchLink = cellFeed.getLink(ILink.Rel.FEED_BATCH, ILink.Type.ATOM);
CellFeed batchResponse = spreadsheetService.batch(new URL(batchLink.getHref()), batchRequest);
boolean isSuccess = true;
for (CellEntry entry1 : batchResponse.getEntries()) {
String batchId = BatchUtils.getBatchId(entry);
if (!BatchUtils.isSuccess(entry1)) {
isSuccess = false;
BatchStatus status = BatchUtils.getBatchStatus(entry);
}
}
spreadsheetService.setHeader("If-Match", null);
Batch copy:
What is the fastest way to update a google spreadsheet with a lot of data through the spreadsheet api?
There is a Bug with cell references such as $A5.
They will not write to the spreadsheet. While both A5 and $A$5 work, references with just one $ in cause a problem. I forget the fine detail.