Google apps script and script db replacement example needed - html

I've got a google apps script UI I am using in a google doc.
I'm trying to replace the current handler which uses the Script DB. Script DB has since been deprecated. The amount of information I was writing was minimal and I figured I would just write the info to google sheets.
Here is the handler from the .html
function addApprover(){
google.script.run.withSuccessHandler(function() {
getApprovers();
$('#approver').val('');
}).addApprover($("#approver").val());
}
.gs
function addApprover(email){
var db = ScriptDb.getMyDb();
var docId = DocumentApp.getActiveDocument().getId();
var ob = {
docId: docId,
approverEmail: email,
status: null,
emailSent: false
}
db.save(ob);
var history = {
docId: docId,
action: 'Added Approver',
email: email,
date: Utilities.formatDate(new Date(), "GMT", "MM-dd-yyyy' 'HH:mm:ss"),
}
db.save(history);
}
I figure that I still call the .gs function and just need to change the function accordingly.
I'm fairly certain that the text box approver holds the email addresses.
How do I access these items?
I'm fairly certain I'm looking for a "for each" statement to iterate through each email address and send them a message and write their name to a specific area of a sheet but I am unsure how to proceed.

Hopefully this will get you started:
function addApprover(email){
var docId = DocumentApp.getActiveDocument().getId();
var ss = SpreadsheetApp.openById('Your Spreadsheet file ID here');
var sheetToWriteTo = ss.getSheetByName('Your sheet name here');
var rowData = [docId, email, null, false];
sheetToWriteTo.appendRow(rowData);
var history = [docId, 'Added Approver', email, Utilities.formatDate(new Date(), "GMT", "MM-dd-yyyy' 'HH:mm:ss")];
sheetToWriteTo.appendRow(rowData);
}
If you want to write the two sets of data to two different sheets, you'll need to get a reference to a second sheet. The data goes into an array, not an object. Although you will see an array called an object in Google documentation also. If you see brackets [], it's an array.
If you have any problems, debug the code with Logger.log() statements and/or debug and a breakpoint, then post another question if it's a major issue, or if it's something minor, make a comment here.

Related

Google sheet sends an email when a new form is submitted if a question has a particular answer

I hope you can help, I have tried to find a solution but I am unable to find one that resolves my issue.
I have a form that collects information if a student request a change to their course, we are a multi school site and depending on the site, a different member of staff is required to approve the change.
Part of the information collect is the site code OAAd for example, and this information is held in column F.
If a submission is received the 'On form submit' trigger, triggers the script. Currently it doesn't seem to work, yet it triggers without error. If I change the trigger 'On edit' and edit a cell in column F it works fine.
eventually I will use ifElse to add in the additional options for each site, but if I could get it to work for one site that would be great.
function sendEmailapproval(e) {
if(e.range.getColumn()==6 && e.value=='OAAd'){
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Email Addresses").getRange("B3");
var recipent = emailRange.getValue();
var html = HtmlService.createTemplateFromFile("Email1.html");
var htmlText = html.evaluate().getContent();
var subject = "New Change Log Request Post 16";
var body = "";
var options = { htmlBody: htmlText}
MailApp.sendEmail(recipent, subject, body, options)
}
}
Background
There are 2 options to add an onFormSubmit trigger:
Using the form settings / script.
Using the attached spreadsheet script.
Both methods gives an event object to communicate with new submmited responese, but threre are differaces between those objet so you need to dicide in advance which method to choose.
You can read more about this topic here.
For your propose, would preder to use the spreadsheet script, since it easier to extract specific values from it.
Solution
First, Here is your correct sendEmailapproval function:
function sendEmailapproval(e) {
const namedValues = e.namedValues
const question = 'Question 3'; // copy-paste from the form
switch (namedValues[question][0])
{
case 'OAAd':
const emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Email Addresses").getRange("B3");
const recipent = emailRange.getValue();
const html = HtmlService.createTemplateFromFile("Email1.html");
const htmlText = html.evaluate().getContent();
const subject = "New Change Log Request Post 16";
const body = "";
const options = { htmlBody: htmlText}
MailApp.sendEmail(recipent, subject, body, options)
break;
// case 'SOMETHING_ELSE':
// break;
default:
}
}
Note that e.namedValues is an object, where each key mapped to an array.
So namedValues[question][0] refers to the string value of namedValues[question].
Second, add a trigger to the spreadsheet that attached to the form.
function createTrigger() {
const sheet = SpreadsheetApp.openById("YOUR_SPREADSHEET_ID").getSheetByName("THE_SHEET_NAME");
ScriptApp.newTrigger("sendEmailapproval")
.forSpreadsheet(sheet)
.onFormSubmit()
.create();
}

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.

Google Docs script editor trying to autogenerate an email from an autogenerated document

Everything I've found about this topic includes having a google sheet populate a google doc which then sends an email. I personally have pasted some code I found around the web into script editor of a doc. Now, upon opening a doc, the user is prompted to answer question boxes. the answers autopopulate a new document that is created. The script then calls for an email to be sent out.
So far, I have the prompts correct, the new document is created, with the correctly filled-in information from the prompt boxes. I have also gotten it to send an email to 1 address, which is all it is supposed to do. The subject line of the email is also correct. The problem is I want the new Google document that is created in the script to be the body of the email, and I just cannot figure out how to make that happen.
This is the code I have in script editor. I have tried numerous things in the last line to make the body of the new document populate the email body, with no luck. Can someone tell me the programming language for how to make this work please?
function myFunction() {
// Display a dialog box for each field you need information for.
var ui = DocumentApp.getUi();
//var response = ui.prompt('Enter Name', 'Enter sales person's name', ui.ButtonSet.OK);
var shiftResponse = ui.prompt('Enter shift, i.e. 7-3 or 3-11');
var peersResponse = ui.prompt('Enter peers on shift');
var participantsResponse = ui.prompt('Enter names of face to face encounters');
var phonelogResponse = ui.prompt('Enter names of people we called on phone log');
var filescreatedResponse = ui.prompt('Enter names of people we created files for');
var notesResponse = ui.prompt('Enter any notes about shift');
var cleanResponse = ui.prompt('Was Crisis Center Cleaned? Enter yes or no');
var authorResponse = ui.prompt('Enter your name');
var date = Utilities.formatDate(new Date(), "GMT", "MM/dd/yyyy");
//Make a copy of the template file
var documentId = DriveApp.getFileById('1lXTJPvwlJrXkRJ807daFsFbfaiC_wl7EAQ4giixLeEc').makeCopy().getId();
//Rename the copied file
DriveApp.getFileById(documentId).setName(date + " " + shiftResponse.getResponseText() + ' Shift Report');
//Get the document body as a variable
var body = DocumentApp.openById(documentId).getBody();
//Insert the entries into the document
body.replaceText('##date##', date);
body.replaceText('##shift##', shiftResponse.getResponseText());
body.replaceText('##peers##', peersResponse.getResponseText());
body.replaceText('##participants##', participantsResponse.getResponseText());
body.replaceText('##phonelog##', phonelogResponse.getResponseText());
body.replaceText('##filescreated##', filescreatedResponse.getResponseText());
body.replaceText('##notes##', notesResponse.getResponseText());
body.replaceText('##clean##', cleanResponse.getResponseText());
body.replaceText('##author##', authorResponse.getResponseText());
MailApp.sendEmail("jason.chrystal#voicesofhopececilmd.org", "Shift Report", body);
}
As it reads in the documentation:
The replaceText methods expects a regex pattern value as the first parameter:
https://developers.google.com/apps-script/reference/document/body#replacetextsearchpattern,-replacement
Also the last parameter of the MailApp.sendMail parameter expects a string and you are giving it a body class object.
Change your first parameter to a correct matching regex pattern and your code will work just fine.
body.replaceText(/^##date##$/, date);
body.replaceText(/^##shift##$/, shiftResponse.getResponseText());
body.replaceText(/^##peers##$/, peersResponse.getResponseText());
etc...
-- regex not tested.
If you are not comfortable with regular expressions you can use body.setText() as an alternative like so:
var oldBodyText = body.getText();
body = body.setText(oldBodyText.replace('##date##', date));
oldBodyText = body.getText();
body = body.setText(oldBodyText.replace('##shift##', shiftResponse.getResponseText()));
oldBodyText = body.getText();
body = body.setText(oldBodyText.replace('##peers##', peersResponse.getResponseText()));
etc...
// And then the last lines:
var newBody = body.getText();
MailApp.sendEmail("jason.chrystal#voicesofhopececilmd.org", "Shift Report", newBody);

Is GAS/google sheet an alternative option to PHP/mySQL?

I keep finding forum results that refer to the google visualiser for displaying my query results. But it just seems to dump the data in a pre-made table. I haven't found a way to write my own custom table dynamically.
In the past, when I have hacked together PHP to make use of mySQL DB, I would simply connect to my DB, run a query into an array, then cycle through the array and write the table in HTML. I could do IF statements in the middle for formatting or extra tweaks to the displayed data, etc. But I am struggling to find documentation on how to do something similar in a google script. What is the equivalent work flow? Can someone point me to a tutorial that will start me down this path?
I just want a simple HTML page with text box and submit button that runs a query on my Google sheet (back in the .gs file) and displays the results in a table back on the HTML page.
Maybe my understanding that GAS/google sheets is an alternative to PHP/mySQL is where I'm going wrong? Am I trying to make a smoothie with a toaster instead of a blender?
Any help appreciated
Welcome David. Ruben is right, it's cool to post up some code you have tried. But I spent many months getting my head around Apps-script and love to share what I know. There are several ways to get the data out of a Google sheet. There is a very well document Spreadsheet Service For GAS. There is also a client API..
There is the approach you mention. I suggest converting the incoming data to JSON so you can do with it as you like.
Unauthenticated frontend queries are also possible. Which needs the spreadsheet to be published and set to anyone with the link can view, and uses the Google visualisation API
var sql = 'SELECT A,B,C,D,E,F,G where A = true order by A DESC LIMIT 10 offset '
var queryString = encodeURIComponent(sql);
var query = new google.visualization.Query('https://docs.google.com/spreadsheets/d/'+ spreadsheetId +'/gviz/tq?tq=' + queryString);
query.send(handleSampleDataQueryResponse);
function handleSampleDataQueryResponseTotal(responsetotal) {
var myData = responsetotal.getDataTable();
var myObject = JSON.parse(myData.toJSON());
console.log(myObject)
}
Other Approaches
In your GAS back end this can get all of your data in columns A to C as an array of arrays ([[row1],[row2],[row3]]).
function getData(query){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var range = sheet.getRange('A1:C');
var data = range.getValues();
// return data after some query
}
In your GAS front end this can call to your backend.
var data = google.script.run.withSuccessHandler(success).getData(query);
var success = (e) => {
console.log(e)
}
In your GAS backend this can add data to your sheet. The client side API will also add data, but is more complex and needs authentication.
function getData(data){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var range = sheet.getRange('A1:C');
var data = range.setValues([[row1],[row2],[row3]]);
return 'success!'
}
In your GAS frontend this can send data to your backend.
var updateData = [[row1],[row2],[row3]]
var data = google.script.run.withSuccessHandler(success).getData(updateData);
var success = (e) => {
console.log(e)
}
Finally
Or you can get everything from the sheet as JSON and do the query in the client. This works okay if you manipulate the data in the sheet as you will need it. This also needs the spreadsheet to be published and set to anyone with the link can view.
var firstSheet = function(){
var spreadsheetID = "SOME_ID";
var url = "https://spreadsheets.google.com/feeds/list/" + spreadsheetID +"/1/public/values?alt=json";
return new Promise((resolve,reject)=>{
$.getJSON(url, (data)=>{
let result = data.feed.entry
resolve(result)
});
})
}
firstSheet().then(function(data){
console.log(data)
})

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.