Data from a Google Sheet "form" to a Google sheet spreadsheet - google-sheets

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()
}

Related

Use Google Sheet Script to Roll Up Multiple GA4 Properties

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

How to apply formatting to a code-generated gmail

I am following these tutorials on how to create an email and populate it with values from a Google Sheet.
https://developers.google.com/apps-script/articles/sending_emails
https://katydecorah.com/code/google-sheets-to-gmail/
It all works as expected, however I cannot apply any formatting to the email such as underline, bold or even tables. Any formatting I apply in the original Google Doc is removed when the email is generated.
EDIT:
Here is the Google-Doc used as the template
Dear {caregiver}
Herewith please find the Weekly Engagement Summary for {fname} {sname}
Week 1: {week_1_score}
Week 2: {week_2_score}
Thank You
And here is the Google-Sheet used with the values
fname|sname|caregiver|email|week_1_score|week_2_score|date drafted
bob|smith|parent1|user1#gmail.com|4|3
john|jones|parent2|user2#gmail.com|2|4
rob|brown|parent3|user3#live.com|3|5
And here is the code-behind script that glue them together and creates the email
// What is the Google Document ID for your email template?
var googleDocId = "<my id>";
// Which column has the email address? Enter the column row header exactly.
var emailField = 'email';
// What is the subject line?
var emailSubject = 'Weekly Engagement Indicator';
// Which column is the indicator for email drafted? Enter the column row header exactly.
var emailStatus = 'date drafted';
/* ----------------------------------- */
// Be careful editing beyond this line //
/* ----------------------------------- */
var sheet = SpreadsheetApp.getActiveSheet(); // Use data from the active sheet
function draftMyEmails() {
var emailTemplate = DocumentApp.openById(googleDocId).getText(); // Get your email template from Google Docs
var data = getCols(2, sheet.getLastRow() - 1);
var myVars = getCols(1, 1)[0];
var draftedRow = myVars.indexOf(emailStatus) + 1;
// Work through each data row in the spreadsheet
data.forEach(function(row, index){
// Build a configuration for each row
var config = createConfig(myVars, row);
// Prevent from drafing duplicates and from drafting emails without a recipient
if (config[emailStatus] === '' || config[emailStatus] !== '' && config[emailField]) {
// Replace template variables with the receipient's data
var emailBody = replaceTemplateVars(emailTemplate, config);
// Replace template variables in subject line
var emailSubjectUpdated = replaceTemplateVars(emailSubject, config);
// Create the email draft
GmailApp.createDraft(
config[emailField], // Recipient
emailSubjectUpdated, // Subject
emailBody // Body
);
sheet.getRange(2 + index, draftedRow).setValue(new Date()); // Update the last column
SpreadsheetApp.flush(); // Make sure the last cell is updated right away
}
});
}
function replaceTemplateVars(string, config) {
return string.replace(/{[^{}]+}/g, function(key){
return config[key.replace(/[{}]+/g, "")] || "";
});
}
function createConfig(myVars, row) {
return myVars.reduce(function(obj, myVar, index) {
obj[myVar] = row[index];
return obj;
}, {});
}
function getCols(startRow, numRows) {
var lastColumn = sheet.getLastColumn(); // Last column
var dataRange = sheet.getRange(startRow, 1, numRows, lastColumn) // Fetch the data range of the active sheet
return dataRange.getValues(); // Fetch values for each row in the range
}
Any help appreciated.
Thanks

Populating a Doc with Google Sheets

I don't know why the text shows up in the Google Doc for First Name and Last Name columns but no text for the notes column. I'm not sure why, I'd also like for row 9, 10, and 11 to populate text depending on which row is selected.
function onOpen() {
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('AutoFill Docs');
menu.addItem('Create New Docs', 'createNewGoogleDocs');
menu.addToUi();
}
function createNewGoogleDocs() {
const googleDocTemplate = DriveApp.getFileById('1ybOPZ-Y3wIZMyZpCW3HUojb72nDF8D9ruipdphpWvBE');
const destinationFolder = DriveApp.getFolderById('1jtR9DjOY7wvu2I9NQvZtrWBkZtDlK2c0');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Enrollments");
const rows = sheet.getDataRange().getDisplayValues();
rows.forEach(function(row, index) {
if (index === 0) return;
if (row[14]) return;
const copy = googleDocTemplate.makeCopy(`${row[1]}, ${row[2]} ABC Bartending and Casino
School`, destinationFolder);
const doc = DocumentApp.openById(copy.getId());
const body = doc.getBody();
const friendlyDate = new Date(row[1]).toLocaleDateString();
body.replaceText('{{Student ID}}', row[0]);
body.replaceText('{{Date}}', friendlyDate);
body.replaceText('{{First Name}}', row[3]);
body.replaceText('{{Last Name}}', row[4]);
body.replaceText('{{Phone}}', row[5]);
body.replaceText('{{Amount Paid}}', row[6]);
body.replaceText('{{Balance}}', row[7]);
body.replaceText('{{Start}}', friendlyDate);
body.replaceText('{{am}}', row[9]);
body.replaceText('{{pm}}', row[10]);
body.replaceText('{{we}}', row[11]);
body.replaceText('{{Email}}', row[12]);
body.replaceText('{{Notes}}', row[13]);
doc.saveAndClose();
const url = doc.getUrl();
sheet.getRange(index + 1, 15).setValue(url);
})
}

Google Script to Parse/Filter Data and Set Several sub SpreadSheets from master Spreadsheet

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
}
}

while using header option with XLSX.utils.json_to_sheet , headers not overriding

I'm trying to change header titles by passing an array of titles to options but it does not override the headers. Instead it inserts new headers before the original data. I am passing the same numbers of header titles.
Here is my code:
const ws: XLSX.WorkSheet = XLSX.utils.json_to_sheet(
json,
{header: headerColumns}
);
const wb: XLSX.WorkBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Transactions');
const excelBuffer: any = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
this.saveAsExcelFile(excelBuffer, excelFileName);
And output looks like below:
The basic job of the "header" option is not to override, rather just shift the starting option of the columns.
i.e. any value passed in the header option will be treated as the first column, provided the value should match with existing keys you have in the data.
XLSX.utils.json_to_sheet([{A:1,B:2}, {B:2,C:3}], {header:['C']});
Here column "C" will be the first column in the excel.
For more look out for detailed description here: https://docs.sheetjs.com/#sheetjs-js-xlsx
This is how I have achieved similar behavior:
const XLSX = require('xlsx');
const wb = XLSX.utils.book_new();
const Heading = [
['Sr No', 'User Name', 'Department', 'Bank', 'Country', 'Region', 'Amount']
];
// creating sheet and adding data from 2nd row of column A.
// leaving first row to add Heading
const ws = XLSX.utils.json_to_sheet(data, { origin: 'A2', skipHeader: true });
// adding heading to the first row of the created sheet.
// sheet already have contents from above statement.
XLSX.utils.sheet_add_aoa(ws, Heading, { origin: 'A1' });
// appending sheet with a name
XLSX.utils.book_append_sheet(wb, ws, 'Records');
const fileContent = XLSX.write(wb, { bookType: 'xlsx', type: 'buffer' });
Very traditional approach but working, please see complete code below:
const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(
this.releaseDateWiseCountList
);
worksheet.A1.v = "Pick Release Date";
worksheet.B1.v = "Task Type";
worksheet.C1.v = "First Shift";
worksheet.D1.v = "Second Shift";
worksheet.E1.v = "Total";
worksheet.F1.v = "Grand Total";
worksheet.G1.v = "Pick %";
const workbook: XLSX.WorkBook = {
Sheets: { 'data': worksheet }, SheetNames: ['data']
};
const excelBuffer: any = XLSX.write(
workbook, { bookType: 'xlsx', type: 'array' }
);
const data: Blob = new Blob([buffer], {type: EXCEL_TYPE});
FileSaver.saveAs(data, 'Result_export_' + new Date().getTime() + EXCEL_EXTENSION);

Resources