Google Apps Script not transferring multiline data from Google Form - google-apps-script

I have written the following code to generate a pdf from a form response. However, my multiline response (genRemarks) and my score (tSCR) are not being used with the template file I've created, while my timestamp (cDate) is.
function onSubmit(e) {
const rg = e.range;
const sh = rg.getSheet();
const cDate = e.namedValues['Timestamp'][0];
const cBodyComments = e.namedValues['genRemarks'][0];
//Consequential/Calculated Data
const cUCS = e.namedValues['Uniform Compliance Score'][0];
const cPCS = e.namedValues['Protocol Compliance Score'][0];
const cDCR = e.namedValues['Dignified Conduct Rating'][0];
const cESR = e.namedValues['Empathy/Sincerity Rating'][0];
const cSWS = e.namedValues['Structured Work Score'][0];
const cCCOR = e.namedValues['Chain of Command Observed Rating'][0];
const cWER = e.namedValues['Work Environment Rating'][0];
const cOTOTS = e.namedValues['On Task and On Time Score'][0];
var tSCR = cUCS + cPCS + cDCR + cESR + cSWS + cCCOR + cWER + cOTOTS;
const invoiceFolderID = '[omitted due to sensitive data]';
const invoiceFolder = DriveApp.getFolderById(invoiceFolderID);
const templateFileID = '[omitted due to sensitive data]';
const newFilename = 'OCSO Report_' + cDate;
const newReportFileID = DriveApp.getFileById(templateFileID).makeCopy(newFilename, invoiceFolder).getId();;
var document = DocumentApp.openById(newReportFileID);
var body = document.getBody();
//start template replacement
body.replaceText('{{Timestamp}}', cDate);
body.replaceText('{{genRemarks}}', cBodyComments);
body.replaceText('{{Score}}', tSCR);
document.saveAndClose();
}
Im a bit new to apps script so any help is welcomed

Try changing this: const cBodyComments = e.namedValues['genRemarks'][0]; to this: const cBodyComments = e.namedValues['genRemarks'][1]; or whatever based upon your observation of
function onFormSubmit(e) {
Logger.log(JSON.stringified(e));
}
I believe the reason I'm seeing them come in columns other than zero was because I edited the form several times and ended up with multiple columns with the same name so this may not be the issue. If this is isn't your problem please let me know and I'll delete my answer.

Related

Function Keeps Looping After Last Row

I am using the following code to fill a Google Docs template with data pulled from a spreadsheet.
function createBulkMembershipCards() {
const template = DriveApp.getFileById("--------");
const docFolder = DriveApp.getFolderById("----------");
const pdfFolder = DriveApp.getFolderById("----------------");
const libroSoci = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("LibroSoci");
const data = libroSoci.getRange(352, 1, libroSoci.getLastRow()-1,19).getDisplayValues();
data.forEach(row => {
createMembershipCard (row[3],row[4],row[0],row[1], row[6],template,docFolder,pdfFolder);
});
}
function createMembershipCard (name,surname,msnumber,timestamp, email,template,docFolder,pdfFolder) {
const file = template.makeCopy(docFolder);
const docFile = DocumentApp.openById(file.getId());
const body = docFile.getBody();
body.replaceText("{name}", name);
body.replaceText("{surname}", surname);
body.replaceText("{msnumber}", msnumber);
body.replaceText("{timestamp}", timestamp);
body.replaceText("{email}", "<<"+email+">>");
docFile.saveAndClose();
docFile.setName(msnumber+" "+name+" "+surname);
const pdfBlob = docFile.getAs(MimeType.PDF);
pdfFolder.createFile(pdfBlob).setName(msnumber+" "+name+" "+surname);
}
I do not understand why, even though I used "getLastRow", the function keeps going on indefinitely after the last populated row.
Please not that I put 352 as the starting row because I want to extract data from that row on.
Is anyone able to help?
Thank you!
Let's say getLastRow() returns 1000. Then you are getting 999 rows. What you want is 1000-351 rows.
Change
const data = libroSoci.getRange(352, 1, libroSoci.getLastRow()-1,19).getDisplayValues();
To
const data = libroSoci.getRange(352, 1, libroSoci.getLastRow()-351,19).getDisplayValues();

Create PDF from Google Sheet Template

I am fairly new to code and App Script, but I've managed to come up with this from research.
Form submitted, Sheet populated, take entry data, copy and append new file, save as pdf, email pdf
I've created examples of what I've been trying to do
Link to form - https://docs.google.com/forms/d/e/1FAIpQLSfjkSBkn3eQ1PbPoq0lmVbm-Dk2u2TP_F_U5lb45SddsTsgsA/viewform?usp=sf_link
link to spreadsheet - https://docs.google.com/spreadsheets/d/1kWQCbNuisZsgWLk3rh6_Iq107HoK7g-qG2Gln5pmYTE/edit?resourcekey#gid=1468928415
link to template - https://docs.google.com/spreadsheets/d/1Ye7DyJQOjA3J_EUOQteWcuASBCfqlA-_lzyNw0REjY8/edit?usp=sharing
However I receive the following error - Exception: Document is missing (perhaps it was deleted, or you don't have read access?)
at Create_PDF(Code:32:34)
at After_Submit(Code:13:21)
App Script Code as follows - If I use a google Doc as a template it works. However I would like to use a spreadsheet as a template, and have the result pdf content fit to page. Please let me know if you need any additional information for this to work.
function After_Submit(e, ){
var range = e.range;
var row = range.getRow(); //get the row of newly added form data
var sheet = range.getSheet(); //get the Sheet
var headers = sheet.getRange(1, 1, 1,5).getValues().flat(); //get the header names from A-O
var data = sheet.getRange(row, 1, 1, headers.length).getValues(); //get the values of newly added form data + formulated values
var values = {}; // create an object
for( var i = 0; i < headers.length; i++ ){
values[headers[i]] = data[0][i]; //add elements to values object and use headers as key
}
Logger.log(values);
const pdfFile = Create_PDF(values);
sendEmail(e.namedValues['Your Email'][0],pdfFile);
}
function sendEmail(email,pdfFile,){
GmailApp.sendEmail(email, "Subject", "Message", {
attachments: [pdfFile],
name: "From Someone"
});
}
function Create_PDF(values,) {
const PDF_folder = DriveApp.getFolderById("1t_BYHO8CqmKxVIucap_LlE0MhslpT7BO");
const TEMP_FOLDER = DriveApp.getFolderById("1TNeI1HaSwsloOI4KnIfybbWR4u753vVd");
const PDF_Template = DriveApp.getFileById('1Ye7DyJQOjA3J_EUOQteWcuASBCfqlA-_lzyNw0REjY8');
const newTempFile = PDF_Template.makeCopy(TEMP_FOLDER);
const OpenDoc = DocumentApp.openById(newTempFile.getId());
const body = OpenDoc.getBody();
for (const key in values) {
body.replaceText("{{"+key+"}}", values[key]);
}
OpenDoc.saveAndClose();
const BLOBPDF = newTempFile.getAs(MimeType.PDF);
const pdfFile = PDF_folder.createFile(BLOBPDF);
console.log("The file has been created ");
return pdfFile;
}
You get the error message with Google Sheets because you are using a Google Doc class to create the PDF, which is not compatible with Google Sheets.
DocumentApp can only be used with Google Docs. I will advise you to change
const OpenDoc = DocumentApp.openById(newTempFile.getId());
for
const openDoc = SpreadsheetApp.openById(newTempFile.getId());
const newOpenDoc = openDoc.getSheetByName("Sheet1");
And depending on the Google Sheet where the "Body" of the information is located. Replace:
const body = OpenDoc.getBody();
for an equivalent like getRange() or any Range class that helps you target the information you need. For example:
// This example is assuming that the information is on the cel A1.
const body = newOpenDoc.getRange(1,1).getValue();
The template for the PDF should be something like this:

Get company name in google sheets using stock ticker

I have a google sheet getting stock information by symbol. I found this code below to get prices but don't really understand how it's working.
function yahooF(ticker) {
const url = `https://finance.yahoo.com/quote/${ticker}?p=${ticker}`;
const res = UrlFetchApp.fetch(url, {muteHttpExceptions: true});
const contentText = res.getContentText();
const price = contentText.match(/<fin-streamer(?:.*?)data-test="qsp-price"(?:.*)>(\d+\.\d+)<\/fin-streamer>/);
console.log(price[1]);
return price[1];
}
Does anyone know a way using a similar method to get specifically the company name, but understanding how to use this to get other data would be great. I'm not interested in using =GOOGLEFINANCE functions as they seem to fail pretty often.
Try, by parsing the json inside the source
function yahooFNameOfCompany(ticker) {
const url = `https://finance.yahoo.com/quote/${ticker}?p=${ticker}`;
const res = UrlFetchApp.fetch(url, {muteHttpExceptions: true}).getContentText();
var jsonString = res.match(/(?<=root.App.main = ).*(?=}}}})/g) + '}}}}'
var data = JSON.parse(jsonString)
console.log(data.context.dispatcher.stores.StreamDataStore.quoteData[ticker].shortName)
}

Google Apps Script: Use getFolderById for multiple variables depending on value

I think I have a very simple issue with Google Apps Script, but I already tried to google the solution for 1.5hrs without success. I guess I search for the wrong terms.
Here my code:
function folderLocations(){
var folder = {
Michael: '1bz9wIBRcRN2V-xxxxxxxxxx',
Chris: '1AEKHiI8iZKjHs-xxxxxxxxxx',
Steve: '1TD8iwjcbR7K5dN-xxxxxxxxxx',
};
return folder;
}
function createNewGoogleDocs() {
//ID of Google Docs Template, what sheet to use + save all values as 2D array
const googleDocTemplate = DriveApp.getFileById('xxxxxxxxxx_XznDn-i0WVtIM');
const sheet = SpreadsheetApp
.getActiveSpreadsheet()
.getSheetByName('Current Overview');
const rows = sheet.getDataRange().getValues();
//Start processing each spreadsheet row
rows.forEach(function(row, index){
//Destination folder ID (can differ from each person)
const destinationFolder = DriveApp.getFolderById(folderLocations().Chris);
// Set custom file name and create file
const copy = googleDocTemplate.makeCopy(`${row[15]} - ${row[3]} Quarterly Review` , destinationFolder);
const doc = DocumentApp.openById(copy.getId());
const body = doc.getBody();
// Replace placeholders with real values
body.replaceText('%NAME%', row[3]);
body.replaceText('%QUARTER%', row[15]);
body.replaceText('%ANSWER_1%', row[16]);
body.replaceText('%ANSWER_2%', row[17]);
[...]
doc.saveAndClose();
})
}
All working fine! BUT: What I want is to "dynamically" change the folder, depending on the value of a cell. It's not always "Chris"...:
const destinationFolder = DriveApp.getFolderById(folderLocations().Chris);
E.g.: If row[4] == Michael, then use the folder ID of "Michael". Somehow I can't get it to work to be "dynamically". 😔
I already tried all this, none working:
const destinationFolder = DriveApp.getFolderById(folderLocations().row[4]);
const destinationFolder = DriveApp.getFolderById(folderLocations(row[4]));
const destinationFolder = DriveApp.getFolderById(folderLocations().`${row[4]}`);
const destinationFolder = DriveApp.getFolderById(folderLocations().toString(row[4]));
etc.
👆🏻 I know what I try to do here is embarrassing. But I am normally not a developer and nobody at my company is familiar with Google Apps Script. That's the last bit I am missing, rest I put together myself using Google.
Thank you SOO much! 🙏🏻
You don't even need a function. Just an object is enough:
const folderLocations = {
Michael: '1bz9wIBRcRN2V-xxxxxxxxxx',
Chris: '1AEKHiI8iZKjHs-xxxxxxxxxx',
Steve: '1TD8iwjcbR7K5dN-xxxxxxxxxx',
};
var id = folderLocations['Chris'];
console.log(id); // 1AEKHiI8iZKjHs-xxxxxxxxxx
const destinationFolder = DriveApp.getFolderById(folderLocations[row[4]]);
This did the trick :
function folderLocations(person){
var folder = {
Michael: '1bz9wIBRcRN2V-xxxxxxxxxx',
Chris: '1AEKHiI8iZKjHs-xxxxxxxxxx',
Steve: '1TD8iwjcbR7K5dN-xxxxxxxxxx',
};
return folder[person];
}
...further below:
const destinationFolder = DriveApp.getFolderById(folderLocations(row[4]));

Save a Google Form as PDF on a Drive's folder using Google Scripts

This is my first approach using Google Scripts so apologize if the question is too easy.
All the steps are explained here: Hacking it: Generate PDFs from Google Forms. I took the following image from there:
The code is posted on the link but I post it here anyways:
function onSubmit(e) { // From https://medium.com/swlh/hacking-it-generate-pdfs-from-google-forms-3ca4fcc5a0aa
const rg = e.range;
const sh = rg.getSheet();
//Get all the form submitted data
//Note: This data is dependent on the headers. If headers, are changed update these as well.
const cName = e.namedValues['Client Name'][0];
const cEmail = e.namedValues['Client Email'][0];
const cAddress = e.namedValues['Client Address'][0];
const cMobile = e.namedValues['Client Mobile'][0];
const sendCopy = e.namedValues['Send client a copy?'][0];
const paymentType = e.namedValues['What is your agreed upon payment schedule?'][0];
const fixedCost = e.namedValues['What was your agreed upon cost for the project?'][0];
const hourlyRate = e.namedValues['Hourly Rate'][0];
const manHours = e.namedValues['Total man hours'][0];
const services = e.namedValues['Select the services'][0];
//Consequential Data
const tax = 18.5
var subtotal = 0;
var taxAmt = 0;
var payableAmt = 0;
//if the user has selected hourly payment model
//Note: Be careful that the responses match the elements on the actual form
switch (paymentType ){
case 'Hourly Rate':
subtotal = hourlyRate*manHours;
taxAmt = subtotal * (tax/100);
payableAmt = +subtotal + +taxAmt;
break;
case 'Fixed Cost':
subtotal = fixedCost;
taxAmt = fixedCost * (tax/100)
payableAmt = +fixedCost + +taxAmt;
break;
}
const invoiceID = 'IN' + Math.random().toString().substr(2, 9);
var formattedDate = Utilities.formatDate(new Date(), "IST", "dd-MMM-yyyy");
//Set the consequential data in the columns of the spreadsheet for record keeping
//Note: These variable are dependent on the sheet's columns so if that changes, please update.
const row = rg.getRow();
const payableAmtCol = 2; //B
const invoiceIDCol = 3; //C
sh.getRange(row,payableAmtCol).setValue(payableAmt);
sh.getRange(row,invoiceIDCol).setValue(invoiceID);
//Build a new invoice from the file
//Folder and file IDs
const invoiceFolderID = '<invoice-folder-id>';
const invoiceFolder = DriveApp.getFolderById(invoiceFolderID);
const templateFileID = '<template-id>';
const newFilename = 'Invoice_' + invoiceID;
//Make a copy of the template file
const newInvoiceFileID = DriveApp.getFileById(templateFileID).makeCopy(newFilename, invoiceFolder).getId();;
//Get the invoice body into a variable
var document = DocumentApp.openById(newInvoiceFileID);
var body = document.getBody();
//Replace all the {{ }} text in the invoice body
body.replaceText('{{Invoice num}}', invoiceID);
body.replaceText('{{Date}}', formattedDate);
body.replaceText('{{Client Name}}', cName);
body.replaceText('{{Client Address}}', cAddress);
body.replaceText('{{Client Mobile}}', cMobile);
body.replaceText('{{Client Email}}', cEmail);
body.replaceText('{{Services}}', services.split(', ').join('\n'));
body.replaceText('{{Subtotal}}', subtotal);
body.replaceText('{{Tax Value}}', taxAmt);
body.replaceText('{{Total}}', payableAmt);
//In the case of hourly rate payment type, let's add an additional message giving the rate and the man hours.
if(paymentType.includes('Hourly Rate')){
//It should look something like this on the invoice
//Hourly Rate
//Rate of Rs.1200/hour
//Completed 50 man hours
const message = paymentType + '\nRate of Rs.' + hourlyRate + '/hour\nCompleted ' + manHours + ' man hours';
body.replaceText('{{Payment Type}}', message);
} else {
body.replaceText('{{Payment Type}}', paymentType);
}
document.saveAndClose();
}
}
All the generated documents are saved in Invoices folder as .docx.
Is there any way to edit the above code to save them as PDF? Of course the PDF already had to be filled with the data provided in the Google Form, like the code does but they have to be saved as PDF, not .docx.
you can use the getAs() to convert what you have saved as doc to pdf.
document.saveAndClose();
const blobPDF=document.getAs(MimeType.PDF);
const pdfFile=pdfFolder.createFile(blobPDF);
This can only work if you save it to a folder. Suggestion below:
var pdfFolder = DriveApp.getFolderById(''); // insert your folder ID which you wish to store the pdf that was created.