Google Apps Script for creating id - google-apps-script

I have a google apps script to automatically create a doc when someone submits the form . But now I want to make an Id no. for each submission as well. There is a section in the form where you have to select one among 4 options (mechanical,nonmechanical, It, Open) . I want to create a sequence of numbers but the serial number will be starting with initial letters of whats he selects in that field. For example if someone choose mechanical then the id will be "mc001". Again if the second person enters it then it will be "it002" Can't find the proper way to execute this thing in apps script. Please help .
FOrm Link : https://docs.google.com/forms/d/e/1FAIpQLSdTDh_c_ThUKu6-_CmsaYnVZKFnHEFji-eb4_i0cvRCePxlaQ/viewform
apps script code
function autoFillGoogleDocFromForm(e) {
//e.values is an array of form values
var timestamp = e.values[0];
var Name = e.values[1];
var class1 = e.values[2];
var team = e.values[3];
var project = e.values[4];
var phone = e.values[5];
var idnumber = e.values[6];
//file is the template file, and you get it by ID
var file = DriveApp.getFileById("1nVMeanqeOL3fgEcjlzoq1u_FKu5E9Ta5IEO0ng-mt6Y");
//We can make a copy of the template, name it, and optionally tell it what folder to live in
//file.makeCopy will return a Google Drive file object
var folder = DriveApp.getFolderById("1NeEHCsNhbQ0bAfcd3o-7X4SAhyEWXx1W")
var copy = file.makeCopy(team + ',' + project, folder);
//Once we've got the new file created, we need to open it as a document by using its ID
var doc = DocumentApp.openById(copy.getId());
//Since everything we need to change is in the body, we need to get that
var body = doc.getBody();
//Then we call all of our replaceText methods
body.replaceText('{{name}}', Name);
body.replaceText('{{class1}}', class1);
body.replaceText('{{team}}', team);
body.replaceText('{{type}}', project);
body.replaceText('{{no}}', phone);
body.replaceText('{{id}}', idnumber);
//Lastly we save and close the document to persist our changes
doc.saveAndClose();
}
I would like to execute this thing and hope to get the id automatically generated in the spreed sheet and then get saved in a section within the doc

Replace the var idnumber line in your function with this:
const idnumber = insertUniqueId_(e, 6);
...to put unique IDs in column G. Here's one possible implementation of the insertUniqueId_() function:
/**
* Inserts a unique ID in the row where a new form response was stored.
*
* #param {Object} e The "on form submit" event object.
* #param {Number} uniqueIdColumnIndex The zero-indexed column number of the unique ID column.
* #return {String[]} The unique ID.
*/
function insertUniqueId_(e, uniqueIdColumnIndex) {
const uniqueIdCell = e.range.offset(0, uniqueIdColumnIndex, 1, 1);
const uniqueId = Utilities.getUuid();
uniqueIdCell.setValue(uniqueId);
return uniqueId;
}
The code will insert UUIDs like 123e4567-e89b-12d3-a456-426614174000. To insert IDs like mc001 or it002, replace Utilities.getUuid() with a call to your own function that returns IDs of your liking. It is likely that you will have to check existing IDs to avoid collisions. See the getId_() function in Create one sequential ID Generator across two different Google Sheets for sample code that avoids collisions.

Related

How do I use apps script to programmatically create/submit a google form response on a google form that collects emails? [duplicate]

I have the following issue. I am trying to create a script that will autofill a template google document using the submission of a google form. I am able to get the script to work for questions that are input with text but am struggling on getting the data from questions in the form that are checkboxes (or multiple choice) to work and fill the google document. Any assistance would be great. For example the variable identified as "offense" is from a question with checkboxes that has about 30 different options, I would like each option that is checked on the form to replace text within my google doc. Thanks.
function autoFillGoogleDocFromForm(e) {
//e.values is an array of form values
var timestamp = e.values[4];
var studentName = e.values[3];
var oe = e.values[16];
var gradelevel = e.values[14];
var program = e.values[15];
var offense = e.values[6];
var action = e.values[18];
var serve = e.values[31];
var makeUp = e.values[32];
var comments = e.values[29];
//file is the template file, and you get it by ID
var file = DriveApp.getFileById('1nPWC0IKc1zUJXYxbGahJsSW4uNWwhxnLM8shcD8kEE4');
//We can make a copy of the template, name it, and optionally tell it what folder to live in
//file.makeCopy will return a Google Drive file object
var folder = DriveApp.getFolderById('1FlpHRKqYrEHttA-3ozU3oUVJlgiqqa-F')
var copy = file.makeCopy(studentName + ', ' + timestamp, folder);
//Once we've got the new file created, we need to open it as a document by using its ID
var doc = DocumentApp.openById(copy.getId());
//Since everything we need to change is in the body, we need to get that
var body = doc.getBody();
//Then we call all of our replaceText methods
body.replaceText('<<Student Name>>', studentName);
body.replaceText('<<Incident Date>>', timestamp);
body.replaceText('<<Student Grade>>', gradelevel);
body.replaceText('<<Open enrolled?>>', oe);
body.replaceText('<<IEP/504?>>', program);
body.replaceText('<<Reason for Referral (Handbook)>>', offense);
body.replaceText('<<Administrative Action>>', action);
body.replaceText('<<Date(s) to be Served>>', serve);
body.replaceText('<<Make up Date(s)>>', makeUp);
body.replaceText('<<Comments>>', comments);
//Lastly we save and close the document to persist our changes
doc.saveAndClose();
}
You need to use the labels assigned to the checkboxes to determine if they have been checked. Same for multiple coice.
You can't use ListItems because you can't set the glyph to a check box so I simply insert text with a checkbox character.
I created a form
I then created an onFormSubmit(e) installed trigger in the spreadsheet to get the form response and put it in the Doc. Here I've simply used an active doc to perform my tests. You will need to adjust the script to handle your template doc.
function onFormSubmit() {
// test data
let e = {"authMode":"FULL","namedValues":{"Timestamp":["8/16/2022 14:40:26"],"Student Grade":["Junior"],"Reason for Referrel":["Bad grades, Disruptive in class, Other"],"Student Name":["Joe Smith"],"Open Enrollment":["Yes"]},"range":{"columnEnd":5,"columnStart":1,"rowEnd":2,"rowStart":2},"source":{},"triggerUid":"12151926","values":["8/16/2022 14:40:26","Joe Smith","Junior","Yes","Bad grades, Disruptive in class, Other"]};
try {
let doc = DocumentApp.getActiveDocument();
let body = doc.getBody();
let referrels = ["Bad grades","Unexcused absence","Disruptive in class","Fighting","Other"];
body.replaceText("<<Student Name>>",e.namedValues["Student Name"]);
body.replaceText("<<Student Grade>>",e.namedValues["Student Grade"]);
body.replaceText("<<Open Enrollment>>",e.namedValues["Open Enrollment"]);
// Notice the regex expression below because findText doesn't seem to handle parenthesis well
let text = body.findText("<<Reason for Referral.*>>");
body.replaceText("<<Reason for Referral.*>>","");
if( text ) {
let index = body.getChildIndex(text.getElement().getParent())+1;
referrels.forEach( item => {
let checked = e.namedValues["Reason for Referrel"][0].indexOf(item);
if( checked >= 0 ) {
let listItem = body.insertListItem(index,item);
index = body.getChildIndex(listItem)+1;
}
}
);
}
}
catch(err) {
Logger.log(err);
}
}

Scipt: Input data in new cell based on value from specific cell and form submit auto fill a google doc

I have a google form that inputs collected answers to a google sheet, with a script that auto fills a doc with the relevant information and emails to the selected recipient. That all works. Here is my problem.
I have one specific question on the Google Form with 5 options. When an option is chosen, I have a specific research/quote that is associated with it. I created a formula in the spreadsheet to pull that into the last column of the spreadsheet - which worked.
But...now when I submit the form with with this new info, the script does not work. I get TypeError: Cannot read property 'values' of undefined
at onFormSubmit(Code:8:21)
I assume that the script is trying to get a value from the column I directed it to, but the formula did not operate fast enough for there to be a value there?
I am guessing I need to make this all happen in the script, so the info is put into the column I need first, then it pulls data from the spreadsheet to create the document and send the email. I just don't know how to start...
I cannot even be considered a novice - a friend shared this code with me and I edit it when needed.
Ex: If answer choice in column F = Read, then column K should show "read quote"; If answer choice in column F = Write, then column K should show "write quote"; and so on.
My current script:
// Global variables
var docTemplate = "1EoBcz0BK4R5hm-q5pR68xnQnR8DlR56XzjxRrgsu4uE"; // *** replace with your template ID ***
var docName = "You got a High 5";
function onFormSubmit(e) { // add an onsubmit trigger
// Values come from the spreadsheet form
var observer = e.values[1]
var teacher = e.values[2]
var email = e.values[2]
var period = e.values[4]
var time = e.values[3]
var cif = e.values[5]
var image = e.values[6]
var comments = e.values[7]
var message = e.values[10]
// Get document template, copy it as a new temp doc, and save the Doc’s id
var copyId = DriveApp.getFileById(docTemplate)
.makeCopy(docName+' for '+teacher)
.getId();
// Open the temporary document
var copyDoc = DocumentApp.openById(copyId);
// Get the document’s body section
var copyBody = copyDoc.getActiveSection();
// Replace place holder keys,
copyBody.replaceText('keyobserver', observer);
copyBody.replaceText('keyteacher', teacher);
copyBody.replaceText('keyemail', email);
copyBody.replaceText('keyperiod', period);
copyBody.replaceText('keytime', time);
copyBody.replaceText('keycif', cif);
copyBody.replaceText('keyimage',image);
copyBody.replaceText('keycomments', comments);
copyBody.replaceText('keymessage', message);
var todaysDate = Utilities.formatDate(new Date(), "GMT", "MM/dd/yyyy");
copyBody.replaceText('keyTodaysDate', todaysDate);
// Save and close the temporary document
copyDoc.saveAndClose();
// Convert temporary document to PDF by using the getAs blob conversion
var pdf = DriveApp.getFileById(copyId).getAs("application/pdf");
// Attach PDF and send the email
var subject = "High 5 - it matters.";
var body = "You got a High 5! See attached PDF. " +
"Please do not reply to this email. You will be asked to supply a response thorugh a link within the attached PDF. " +
"'Do all the good you can. By all the means you can. In all the ways you can. In all the places you can. At all the times you can. To all the people you can. As long as you ever can. -John Wesley'";
MailApp.sendEmail(email, subject, body, {htmlBody: body, attachments: pdf});
// Delete temp file
DriveApp.getFileById(copyId).setTrashed(true);
}
The form submit trigger's event object will only capture the responses from the form written to the Google Sheet, not formulas done only in the sheet. That is why you are getting undefined error.
So you need to do your logic in the script itself:
var message;
if (cif == 'Read') {
message = 'read quote'
}
else if (cif == 'Write') {
message = 'write quote'
}
This is extremely simplistic, if you have a lot of choices for column F you might need to use switch instead of if statement.

Extra commas after concatenating a range of cells in Sheet

For reference: Here is my Google Form.
Here is a Google PE Blank Sheet which has been tied to the output of my Google Form (answers will output on tab Form Responses 2).
I've built up a Google Form capable of collecting a good bit of data. It is meant to be filled out by a manager requesting SAP permissions for a new hire.
They proceed through the Form and enter their primary department (SAP Stream) which leads them to the next page/section that allows the manager to check off what the employee's required permissions will be.
Sometimes, an employee will interface with multiple departments, so the option for a supporting/secondary SAP Stream exists on the second page. This can be continued as needed until all of an employee's requisite roles/permissions are collected in a Google Sheet.
In the Sheet, you will see that:
Columns A-F include basic information
Columns H-T are for the Job Responsibilities
Columns U-AG are a range for the information of one's Supporting SAP Stream
Columns AH-AT are the Supporting SAP Stream's Job Responsibilities
Columns AU-BG collect the Secondary Supporting SAP Stream
I had built Google Script language to collect each of these bits of information in variables which would then get declared as global variables so they could be passed along to an HTML page in the App Script.
Finally, an email consisting of this HTML page would be sent out to the manager in charge of designating SAP permissions.
Everything is working fine since my last update. The email gets sent out and the variables are collecting the information from the range of cells I have targeted. But the output is full of commas separating the returned values.
Screenshot of email result
Is it that the cells which have nothing are being included and separated with commas? Or is there something else I've done wrong with my variables?
Edit:
Here is a sample of some of the relevant portions of my code:
function sendEmail_v3() {
// get the spreadsheet information
const ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
//const responseSheet = ss.getSheetByName('Form Responses 1');
const data = ss.getDataRange().getValues();
//console.log(data);
// Loop over the rows
data.forEach((row,i) => {
// Identify whether notification has been sent
if (row[59] === '') {
// Get the Form info
var emailTo = "xxxxxx#gmail.com"
var subject = 'SAP Role Request Subject';
const Timestamp = row[0];
var emailAddress = row[1];
var name = row[2];
var department = row[3];
var userID = row[4];
var SAP_Stream = row[5];
var valuesA = ss.getRange(i,8,1,13).getValues();
var jobResponsibilities = valuesA;
var valuesB = ss.getRange(i,21,1,13).getValues();
var supportingSAP = valuesB;
var valuesC = ss.getRange(i,34,1,13).getValues();
var secondaryJob = valuesC;
var valuesD = ss.getRange(i,47,1,13).getValues();
var secondarySAP = valuesD;
formTime = Timestamp;
formEmail = emailAddress;
formName = name;
formUserID = userID;
formSAP_Stream = SAP_Stream;
formDepartment = department;
formResponsibilities = jobResponsibilities;
formSupportingSAP = supportingSAP + "," + secondarySAP;
formSecondaryJob = secondaryJob;
let body = '';
// Write the email in email.html
var html = HtmlService.createTemplateFromFile("email.html");
var htmlText = html.evaluate().getContent();
var options = {htmlBody: htmlText};
// Send the email
GmailApp.sendEmail(emailTo,subject,'',{htmlBody: htmlText});
// Mark as Notified
const g = 'Notification sent';
ss.getRange(i + 1,60).setValue(g);
}
});
}
The global variables get pulled into a separate part of the Google Script in an HTML file which gets sent out in the email. The email generated looks like this:
Screenshot of email result
I imagine there is another function or some code I can add to the lines with the values variables which will help to avoid the NULL results.
var valuesA = ss.getRange(i,8,1,13).getValues();
You can use filter, concat and join instead. In your case, since the data is also 2D array, you additionally need to do flat on them before using concat. Check the code below. Use concat instead of adding 2 arrays directly using +. I have compared yours and the code below on Logs and Output section:
Code:
formSupportingSAP = supportingSAP.flat().concat(secondarySAP.flat()).filter(Boolean).join(", ");
Logs and Output:
References/Notes:
flat() to get the 1 dimensional array equivalent
concat() to combine the 2 arrays
filter() to filter out blank cells
join() to combine into a string with a delimiter ", "

How to generate a Google Doc from a Form response, containing all Form items and responses?

I am currently using the following to send an email to a distribution list containing the headers/questions and corresponding answers when a Google Form is submitted:
function sendFormByEmail(e) {
// Subject field of email
var emailSubject = "New Student Case Filed";
// Comma-separated list of email addresses for distribution.
var yourEmail = "my email address";
// Spreadsheet key, found in the URL when viewing your spreadsheet.
var docKey = "my spreadsheet key";
// If you want the script to auto send to all of the spreadsheet's editors, set this value as 1.
// Otherwise set to 0 and it will send to the yourEmail values.
var useEditors = 0;
// Have you added columns that are not being used in your form? If so, set this value to
// the NUMBER of the last column that is used in your form.
// for example, Column C is the number 3
var extraColumns = 40;
if (useEditors) {
var editors = DocsList.getFileById(docKey).getEditors();
if (editors) {
var notify = editors.join(',');
} else var notify = yourEmail;
} else {
var notify = yourEmail;
}
// The variable e holds all the submission values in an array.
// Loop through the array and append values to the body.
// Need to omit headers with no response*
var s = SpreadsheetApp.getActive().getSheetByName("StudentCases");
if (extraColumns){
var headers = s.getRange(1,1,1,extraColumns).getValues()[0];
} else var headers = s.getRange(1,1,1,40).getValues()[0];
var message = "";
for(var i in headers) {
message += headers[i] + ' : '+ e.values[i].toString() + '\n\n';
}
Now I also want a Google Doc created containing the headers and responses. So far, I've been able to create the Doc, add the title, and add a paragraph, but now I need to replicate the array of headers/responses in the Google Doc as well.
// Create a new Google Doc named 'Case File' * need to figure out how to pull last name response from form.
var doc = DocumentApp.create('Case File: Last Name*');
// Access the body of the Doc, add a paragraph, *need to append array of headers/answers
var body = doc.getBody().body.appendParagraph();
// Get the URL of the Google Doc to include in Email
var url = doc.getUrl();
// Get ID of Doc to attach to email
var id = doc.getId()
One other issue I'd like to solve; I only need the headers/questions that contain a response, as many of them will not necessarily warrant an answer. So in other words, IF there is no answer, THEN do not append to email.
It seems you provided a list of general requirements without much in the way of what you tried with the results you got. StackOverflow will be helpful to you if you provide more pointed questions about what exactly you have tried..
Can you share the exact code you have tried please? What were your results?
From a high level, I would proceed with this general workflow.
Draft a gDoc template using placeholders in a variables nomenclature of your choice ~FName ~LName etc..
Use an onFormSubmit trigger to make a copy of the gDoc template when a new form is submitted.
Replace the ~FName ~LName placeholders in the copied gDoc with content captured in the form
Save the copied gDoc as a PDF
email the PDF to the email address provided in the form submission

Make Folders from Spreadsheet Data in Google Drive?

I am just beginning to learn Javascript and Google Apps Script. I have looked at and played with a few scripts, and so am attempting to create my own. What I want to do is take a list of students (Name and Email) and run a script to create "dropboxes", or folders that uses their name and is shared to their email address. This way, they can easily submit work to me in an organized manner. I have written this rough script, which I know will not work.
I was wondering if anyone can give me some tips?
function createDropbox () {
// Get current spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = sh1.getDataRange().getValues();
// For each email address (row in a spreadsheet), create a folder, name it with the data from the Class and Name column, then share it to the email
for(n=1;n<data.length;++n){
var class = sheet.getSheetValues(n, 1, 1, 1);
var name = sheet.getSheetValues(n, 2, 1, 1);
var email = sheet.getSheetValues(n, 3, 1, 1);
var folder = DocsList.createFolder('Dropbox');
folder.createFolder(class . name);
var share = folder.addEditor(email);
}
}
You've got the basic structure of your script right, it's only the semantics and some error handling that need to be worked out.
You need to determine how you want to access the contents of your spreadsheet, and be consistent with that choice.
In your question, you are first getting all the contents of the spreadsheet into a two-dimensional array by using Range.getValues(), and then later trying to get values directly from the sheet multiple additional times using .getSheetValues().
Since your algorithm is based on working through values in a range, use of an array is going to be your most effective approach. To reference the content of the data array, you just need to use [row][column] indexes.
You should think ahead a bit. What will happen in the future if you need to add more student dropboxes? As your initial algorithm is written, new folders are created blindly. Since Google Drive allows multiple folders with the same name, a second run of the script would duplicate all the existing folders. So, before creating anything, you will want to check if the thing already exists, and handle it appropriately.
General advice: Write apps script code in the editor, and take advantage of auto-completion & code coloring. That will help avoid mistakes like variable name mismatches (ss vs sh1).
If you are going to complete the exercise yourself, stop reading here!
Script
This script has an onOpen() function to create a menu that you can use within the spreadsheet, in addition to your createDropbox() function.
The createDropbox() function will create a top level "Dropbox" folder, if one does not already exist. It will then do the same for any students in the spreadsheet, creating and sharing sub-folders if they don't already exist. If you add more students to the spreadsheet, run the script again to get additional folders.
I've included comments to explain some of the tricky bits, as a free educational service!
/**
* Create menu item for Dropbox
*/
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Create / Update Dropbox",
functionName : "createDropbox"
}];
sheet.addMenu("Dropbox", entries);
};
function createDropbox () {
// Get current spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = ss.getDataRange() // Get all non-blank cells
.getValues() // Get array of values
.splice(1); // Remove header line
// Define column numbers for data. Array starts at 0.
var CLASS = 0;
var NAME = 1;
var EMAIL = 2;
// Create Dropbox folder if needed
var DROPBOX = "Dropbox"; // Top level dropbox folder
try {
// getFolder throws an exception if folder not found.
var dropbox = DocsList.getFolder(DROPBOX);
}
catch (e) {
// Did not find folder, so create it
dropbox = DocsList.createFolder(DROPBOX);
}
// For each email address (row in a spreadsheet), create a folder,
// name it with the data from the Class and Name column,
// then share it to the email
for (var i=0; i<data.length; i++){
var class = data[i][CLASS];
var name = data[i][NAME];
var email = data[i][EMAIL];
var folderName = class + ' ' + name;
try {
var folder = DocsList.getFolder(DROPBOX + '/' + folderName);
}
catch (e) {
folder = dropbox.createFolder(folderName)
.addEditor(email);
}
}
}
Even though the question is ~6 years old it popped up when I searched for "create folders from sheets data" .
So , taking the answer as inspiration, I had to update the code to allow for changes in the Google API, i.e. no more DocsList.
So here it is.
function createMembersFolders () {
// Get current spreadsheet
var MembersFolder = DriveApp.getFolderById("1Q2Y7_3dPRFsblj_W6cmeUhhPXw2xhNTQ"); // This is the folder "ADI Members"
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = ss.getDataRange().getValues() // Get array of values
// Define column numbers for data. Array starts at 0.
var NAME = 1;
// For each name, create a folder,
for (var i=1; i<data.length; i++){ // Skip header row
var name = data[i][NAME];
Logger.log(name);
if(MembersFolder.getFoldersByName(name).hasNext()){
Logger.log("Found the folder, no need to create it");
Logger.log(Object.keys(MembersFolder.getFoldersByName(name)).length);
} else {
Logger.log("Didn't find the folder so I'll create it");
var newFolder = MembersFolder.createFolder(name);
}
}
}
Note that I'm taking the parent folder directly using it's ID (which you get from the URL when viewing the folder).
You could also do this by name using the DriveApp.getFoldersByName("Folder Name").hasNext() condition which checks if the folder exist. If it does exist then you can access that folder you have found via DriveApp.getFoldersByName("Folder Name").next()
I didn't find that use of hasNext and next very intuitive but there it is.