Data from spreadsheet doesn't populate in the body of the email - javascript

I am trying to email some details regularly and my script doesn't seem to work well. I just need the display value of A3:C7 to be sent in a click of a button but the email isn't showing exactly the way it was on my spreadsheet. I am new on Coding . Below is the code:
function sendReport (){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var raw = sheet.getSheetByName('Raw Data');
var subType = raw.getRange('C3');
var jobType = subType.getValue();
var subID = raw.getRange('C4');
var jobID = subID.getValue();
var subBody = sheet.getSheetByName('Email Sheet');
var body1 = subBody.getRange('A3:C7');
var body = body1.getDisplayValues();
var subject = "RCA Report " + jobType + "-" + jobID
var message = "Hi TL, below is the " + subject
var closing = "For any clarifications, please do not hesitate to reach out to any of the QAs. Thanks! ";
var emailBody = message + body + closing;
MailApp.sendEmail("sample#sample.com",subject,emailBody)
};
The result looks like this:
Hi TL, below it the RCA report + jobType + "-" + jobIDCategory.Count.Judgment.16.0.27%.Did Not Load.....
So the data which looks like a table in my spreadsheet was somehow written continuously in a single line on my email.
Desired result:
Message...
Table A3:C7 (5 rows/3 columns including header)
Message...

To provide spreadsheet data in a table inside your e-mail, you can build an HTML <table> in your e-mail body, and populate data from the spreadsheet into the HTML table.
To do this, you need to iterate over the data in the selected spreadsheet range.
Currently you have this:
var body1 = subBody.getRange('A3:C7');
To iterate over this data, you can use 2 loops. The first will iterate over each row, and the second will iterate over each cell in the current row:
var dataRows = body1.getValues();
for (var i = 0; i < dataRows.length; i++) {
var dataRow = dataRows[i];
for (var j = 0; j < dataRow.length; j++) {
var cellContents = dataRow[j];
}
}
You can use these basic loops to build a string containing an HTML table:
var style = 'style="border: 1px solid black; border-collapse: collapse; padding: 5px;"';
var tableOutput = '<br><br><table ' + style + '>';
var dataRows = body1.getValues();
for (var i = 0; i < dataRows.length; i++) {
tableOutput = tableOutput + '<tr>';
var dataRow = dataRows[i];
for (var j = 0; j < dataRow.length; j++) {
var cellContents = dataRow[j];
tableOutput = tableOutput + '<td ' + style + '>' + cellContents + '</td>';
}
tableOutput = tableOutput + '</tr>';
}
tableOutput = tableOutput + '</table><br>';
Finally, because we are using HTML embedded in the body of the e-mail, we need to use the htmlBody parameter (documented here):
htmlBody: if set, devices capable of rendering HTML will use it instead of the required body argument
To do this, we need to change how we send the e-mail:
var emailBody = message + tableOutput + closing;
MailApp.sendEmail({
to: "sample#sample.com",
subject: subject,
htmlBody: emailBody
});
Here, we are explicitly using htmlBody as the body parameter, instead of the default plain string parameter.
Assuming the following input:
This will build the following e-mail body:
In my example I provided only the most basic styling to the table, to show black border lines around each cell, with some cell padding. You can obviously change that to whatever styling you wish to use.

Related

Email Sender sends in a random order

After filling out a form the script sends an email but in a random order that does not make sense at all. When I try to manipulate the code I get errors and the script stops working.
function onFormSubmit(e) {
var values = e.namedValues;
var htmlBody = '<ol>';
for (Key in values) {
var label = Key;
var data = values[Key];
htmlBody += '<li>' + label + ": " + data + '</li>';
};
htmlBody += '</ol>';
MailApp.sendEmail('email#email.com','WORK REQUEST: ATTENTION NEEDED', '', {htmlBody:htmlBody})
}
Instead of e.namedValues use e.values together with reading the form response sheet columns headers
function onFormSubmit(e) {
var sheet = e.range.getSheet();
var headersRange = sheet.getRange(1,1,1,e.values.length);
var headers = headersRange.getValues()[0];
var htmlBody = '<ol>';
for (var i = 0; i < e.values.length; i++) {
htmlBody += '<li>' + headers[i] + ": " + e.values[i] + '</li>';
};
htmlBody += '</ol>';
MailApp.sendEmail('email#email.com','WORK REQUEST: ATTENTION NEEDED', '', {htmlBody:htmlBody})
}
Why?
e.namedValues returns and object. Object properties are unordered.
By default, the columns order is the same as the questions order

Google Scripts to draft replies to emails with keyword in subject and inputting data from Google Sheets

I'm new to Google Apps Script so I'm looking for some advice. There's multiple parts and I managed to do some of it but I'm stuck on others. Any help would be much appreciated.
I'm trying to make a script that:
drafts a reply to emails that contain specific keywords (found in the body or the subject line).
I also want it to include a template with data inputted from a Google Sheets file.
It would be preferable if the draft can be updated without making a duplicate whenever the Sheet is modified.
I plan on also including a row of values (the first one) that correspond to the Subject columns in the second row the but I haven't gotten to it yet.
Some details about the Google Sheet:
Each row corresponds to a different person and email address that regularly emails me.
The first three columns are details about the person which I include in the body of my draft.
Each column after that represents a different string or keyword I expect to find in the subject of the emails sent to me.
The rows underneath contain two patterned code-words separated by a space in one cell that I want to be able to choose from. Such as:
3 letters that can contain permutations of the letters m, g, r (for ex: mmg, rgm, rgg, gmg)
and 0-3 letters with just p's (for ex: p, pp, ppp, or blank)
I want to be able to detect the different codes and assign it to a variable I can input into my draft.
What I have so far:
I'm able to draft replies for emails within my specified filter. However, I only have it set up to reply to the person's most recent message. I want to be able for sort through the filter for specific emails that contain a keyword in the subject line when it loops through the columns.
I'm able to input static strings from the Sheet into the body of my email but I'm still having trouble with the patterned codewords.
I was able to loop through more than one row in earlier version but now it's not. I'll look over it again later.
Here's my code:
function draftEmail() {
var sheet = SpreadsheetApp.getActiveSheet(); // Use data from the active sheet
var startRow = 1; // First row of data to process
var numRows = sheet.getLastRow() - 1; // Number of rows to process
var lastColumn = sheet.getLastColumn(); // Last column
var dataRange = sheet.getRange(startRow, 1, numRows, lastColumn) // Fetch the data range of the active sheet
var data = dataRange.getValues(); // Fetch values for each row in the range
// Work through each row in the spreadsheet
for (var i = 2; i < data.length; ++i) {
var row = data[i];
// Assign each row a variable
var grader = row[0]; // Col A: Grader's name
var firstName = row[1]; // Col B: Student's first name
var studentEmail = row[2]; // Col C: Student's email
var grade = row[3].split(' '); // Col D: Grade
var pgrade = grade[1];
var hgrade = grade[0];
for (var n = 1; n < data.length; ++n) {
var srow = data[n];
var subjectCol = srow[3];
var threads = GmailApp.getUserLabelByName('testLabel').getThreads();
for (i=0; i < threads.length; i++)
{
var thread = threads[i];
var messages = thread.getMessages(); // get all messages in thread i
var lastmsg = messages.length - 1; // get last message in thread i
var emailTo = WebSafe(messages[lastmsg].getTo()); // get only email id from To field of last message
var emailFrom = WebSafe(messages[lastmsg].getFrom()); // get only email id from FROM field of last message
var emailCC = WebSafe(messages[lastmsg].getCc()); // get only email id from CC field of last message
// form a new CC header for draft email
if (emailTo == "")
{
var emailCcHdr = emailCC.toString();
} else
{
if (emailCC == "")
{
var emailCcHdr = emailTo.toString();
} else
{
var emailCcHdr = emailTo.toString() + "," + emailCC.toString();
}
}
var subject = messages[lastmsg].getSubject().replace(/([\[\(] *)?(RE|FWD?) *([-:;)\]][ :;\])-]*|$)|\]+ *$/igm,"");
// the above line remove REs and FWDs etc from subject line
var emailmsg = messages[lastmsg].getBody(); // get html content of last message
var emaildate = messages[lastmsg].getDate(); // get DATE field of last message
var attachments = messages[lastmsg].getAttachments(); // get all attachments of last message
var edate = Utilities.formatDate(emaildate, "IST", "EEE, MMM d, yyyy"); // get date component from emaildate
var etime = Utilities.formatDate(emaildate, "IST", "h:mm a"); // get time component from emaildate
if (emailFrom.length == 0)
{
// if emailFrom is empty, it probably means that you may have written the last message in the thread. Hence 'you'.
var emailheader = '<html><body>' +
'On' + ' ' +
edate + ' ' +
'at' + ' ' +
etime + ',' + ' ' + 'you' + ' ' + 'wrote:' + '</body></html>';
} else
{
var emailheader = '<html><body>' +
'On' + ' ' +
edate + ' ' +
'at' + ' ' +
etime + ',' + ' ' + emailFrom + ' ' + 'wrote:' + '</body></html>';
}
var emailsig = '<html>' +
'<div>your email signature,</div>' +
'</html>'; // your email signature i.e. common for all emails.
// Build the email message
var emailBody = '<p>Hi ' + firstName + ',<p>';
emailBody += '<p>For ' + subjectCol + ', you will be graded on #1, 2, and 3: <p>';
emailBody += '<p>Participation: ' + pgrade + '</p>';
emailBody += '<p>HW grade: ' + hgrade + '</p>';
emailBody += '<p>If you have any questions, you can email me at ' + grader + '#email.com.<p>';
emailBody += '<p>- ' + grader;
var draftmsg = emailBody + '<br>' + emailsig + '<br>' + emailheader + '<br>' + emailmsg + '\n'; // message content of draft
// Create the email draft
messages[lastmsg].createDraftReply(
" ", // Body (plain text)
{
htmlBody: emailBody // Options: Body (HTML)
}
);
}
}
}
function WebSafe(fullstring)
{
var splitString = fullstring.split(",");
var finalarray = [];
for (u=0; u < splitString.length; u++)
{
var start_pos = splitString[u].indexOf("<") + 1;
var end_pos = splitString[u].indexOf(">",start_pos);
if (!(splitString[u].indexOf("<") === -1 && splitString[u].indexOf(">",start_pos) === -1)) // if < and > do exist in string
{
finalarray.push(splitString[u].substring(start_pos, end_pos));
} else if (!(splitString[u].indexOf("#") === -1))
{
finalarray.push(splitString[u]);
}
}
var index = finalarray.indexOf(grader + "#email.com"); // use your email id. if the array contains your email id, it is removed.
if (index > -1) {finalarray.splice(index, 1);}
return finalarray
}
}
I've never coded in JavaScript before or used Google Scripts so I mostly looked at similar examples.
Thank you for any feedback.
I prefer reading code that isn't too nested. So I took the liberty to re-write your code and make it easier to read.
Your main function:
function mainFunction(){
// Use data from the active sheet
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var threads = GmailApp.getUserLabelByName('<YOUR-LABEL-HERE>').getThreads();
var subject1 = data[1][3];
// Work through each row in the spreadsheet omit headers
for (var i = 2; i < data.length; ++i) {
// Get grader's data
var grader = getGrader(data[i]);
console.log(grader);
// Loop through threads
for (j=0; j < threads.length; j++){
var thread = threads[j];
// Get last message in thread
var messages = thread.getMessages();
var lastMsg = messages[messages.length - 1];
var email = new Email(grader, lastMsg, subject1);
// Create the draft reply.
var draftMessageBody = createDraftMessage(email);
lastMsg.createDraftReply(draftMessageBody);
}
}
}
Support functions:
Function getGrader:
function getGrader(array){
var row = array
var grader = {}
grader.grader = row[0];
grader.firstName = row[1];
grader.studentEmail = row[2];
var grade = row[3].split(' ');
grader.pgrade = grade[1];
grader.hgrade = grade[0];
return grader
}
Function webSafe:
function webSafe(fullstring, grader){
var splitString = fullstring.split(",");
var finalarray = [];
for (u=0; u < splitString.length; u++){
var start_pos = splitString[u].indexOf("<") + 1;
var end_pos = splitString[u].indexOf(">",start_pos);
// if < and > do exist in string
if (!(splitString[u].indexOf("<") === -1 && splitString[u].indexOf(">",start_pos) === -1)){
finalarray.push(splitString[u].substring(start_pos, end_pos));
} else if (!(splitString[u].indexOf("#") === -1)){
finalarray.push(splitString[u]);
}
}
// use your email id. if the array contains your email id, it is removed.
var index = finalarray.indexOf(grader.grader + "#mangoroot.com");
if (index > -1) {
finalarray.splice(index, 1);
}
return finalarray
}
Function Email: Behaves like a class
var Email = function(grader, lastMsg, subject){
this.signature = "your_email_signature,";
this.grader = grader;
this.to = webSafe(lastMsg.getTo(), this.grader);
this.from = webSafe(lastMsg.getFrom(), this.grader);
this.cc = webSafe(lastMsg.getCc(), this.grader);
this.subject = lastMsg.getSubject().replace(/([\[\(] *)?(RE|FWD?) *([-:;)\]][ :;\])-]*|$)|\]+ *$/igm,"");
this.message = lastMsg.getBody();
this.date = lastMsg.getDate();
this.attachments = lastMsg.getAttachments();
this.subject1 = subject;
this.ccHeader = function() {
var ccHeader = "";
if (this.to == "" || this.cc == ""){
ccHeader = this.cc.toString();
}
else {
ccHeader = this.to.toString() + "," + this.cc.toString();
}
return ccHeader
}
this.eDate = function() {
return Utilities.formatDate(this.date, "IST", "EEE, MMM d, yyyy");
}
this.eTime = function() {
return Utilities.formatDate(this.date, "IST", "h:mm a");
}
this.header = function() {
var header = ''.concat('On ');
if (this.from.length == 0){
header += this.eDate().concat(' at ',this.eTime(),', you wrote: ');
}
else {
header += this.eDate().concat(' at ',this.eTime(),', ',this.from,' wrote: ');
}
return header
}
this.body = function(){
var grader = this.grader;
var body = '<div>'.concat('<p>Hi ',grader.firstName,',</p>');
body += '<p>For '.concat(this.subject1,', you will be graded on #1, 2, and 3: </p>');
body += '<p>Participation: '.concat(grader.pgrade,'</p>');
body += '<p>HW grade: '.concat(grader.hgrade,'</p>');
body += '<p>If you have any questions, you can email me at '.concat(grader.grader,'#mangoroot.com.</p>');
body += '<p>- '.concat(grader.grader,'</p>','</div>');
return body;
}
}
Function createDraftMessage:
function createDraftMessage(email){
var draft = '<html><body>'.concat(email.body);
draft += '<br>'.concat(email.signature);
draft += '<br>'.concat(email.header);
draft += '<br>'.concat(email.message);
draft += '<br>'.concat('</body></html>');
return draft;
}
Now when you run mainFunction() you should get your expected drafts.
Notes:
It is good practice to keep functions flat, flat is better than nested. Makes the code more readable and maintainable.
Also be consistent in your variable naming style.
var emailMsg = ''; // Good.
var emailmsg = ''; // Hard to read.
Have a read about classes

How to Log value of all cells in a column on google app scripts

I am trying to get all the values in column L which is indexed as column 11(var check). However I am only getting the value of the first cell in the column and it stops looping through. I have created a comment for a reference.
Could someone please help me?
Please ignore:
It looks like your post is mostly code; please add some more details.
It looks like your post is mostly code; please add some more details.
It looks like your post is mostly code; please add some more details.
function myFunction(e) {
var sheet = SpreadsheetApp.openById("1naSWINA8_uxeLUsj0lFntqILyDj2nirb56uvkBel79Y").getSheetByName("CRM Feedback");
var ss = SpreadsheetApp.getActive().getSheetByName("Form Responses 1");
var data = sheet.getRange(4, 1, ss.getLastRow(), ss.getLastColumn()).getValues();
var manager_email = "XXXXX";
for( var i = 0; i< data.length;i++ ) {
var timestamp = data[i][0];
var requesterEmail = data[i][1];
var starRating = data[i][2];
var requestCatergory = data[i][3];
var description = data[i][4];
var label = data[i][5];
var ticketId = data[i][6];
var comment = data[i][7];
var status = data[i][8];
var priority = data[i][9];
var office = data[i][10];
//I am trying to log all the values in column 11
var check = data[i][11];
Logger.log(check)
var checkTimestamp = data[i][0]
if(check == false){
continue;
} else {
var subject = "CT IT feedback - ";
var body = "<body>";
body += "<br><b>Requester email:</b> " + requesterEmail
body += "<br><b>Star Rating:</b> " + starRating
body += "<br><b>Request Category</b> " + requestCatergory
body += "<br><b>Description:</b> " + description
body += "<br><b>label: </b> " + label
body += "<br><b>Ticket ID: </b>" + ticketId
body += "<br><b>Comment: </b>" + comment
body += "<br><b>status: </b>" + status
body += "<br><b>priority:</b> " + priority
body += "<br><b>office: </b>" + office
body += "</body>";
MailApp.sendEmail(manager_email, subject, body, {htmlBody:body})
var sent_string = "sent";
ss.getRange(i + 1, 12).setValue(sent_string)
if (sent_string){
return
}
}
}
}
You want to loop through all data rows and send emails for each row where the status is not set to "sent" yet
You want to set the status of a row to "sent" after sending an email
The problem is that
you have a return statement which makes you exit the for loop and stop iterating
You seem not to change the status for the right sheet and the right row
I suggest you to modify your code as following:
function myFunction(e) {
var sheet = SpreadsheetApp.openById("1naSWINA8_uxeLUsj0lFntqILyDj2nirb56uvkBel79Y").getSheetByName("CRM Feedback");
var ss = SpreadsheetApp.getActive().getSheetByName("Form Responses 1");
var data = sheet.getRange(4, 1, ss.getLastRow(), ss.getLastColumn()).getValues();
var manager_email = "XXX";
for( var i = 0; i< data.length;i++ ) {
var timestamp = data[i][0];
var requesterEmail = data[i][1];
var starRating = data[i][2];
var requestCatergory = data[i][3];
var description = data[i][4];
var label = data[i][5];
var ticketId = data[i][6];
var comment = data[i][7];
var status = data[i][8];
var priority = data[i][9];
var office = data[i][10];
//I am trying to log all the values in column 11
var check = data[i][11];
Logger.log(check)
var checkTimestamp = data[i][0]
if(check != "sent"){
var subject = "CT IT feedback - ";
var body = "<body>";
body += "<br><b>Requester email:</b> " + requesterEmail
body += "<br><b>Star Rating:</b> " + starRating
body += "<br><b>Request Category</b> " + requestCatergory
body += "<br><b>Description:</b> " + description
body += "<br><b>label: </b> " + label
body += "<br><b>Ticket ID: </b>" + ticketId
body += "<br><b>Comment: </b>" + comment
body += "<br><b>status: </b>" + status
body += "<br><b>priority:</b> " + priority
body += "<br><b>office: </b>" + office
body += "</body>";
MailApp.sendEmail(manager_email, subject, body, {htmlBody:body})
var sent_string = "sent";
//are you sure you want to set the stutus in ss and not in sheet to 'sent'?
//Keep in mind that you start with row 4
sheet.getRange(i +4, 12).setValue(sent_string)
}
}
}

sort key value pairs in Google Script

I have the below script running on a google sheet to automatically send an email with the sheet info to a specific emails. It is working perfectly but I am trying to optimize it. The problem I am having is every-time the email is sent the order of the text (Key value pairs) is different I am trying to order the key value pair based on the way they are in the email.
function onFormSubmit(e) {
var values = e.namedValues;
var sendFrom = '';
var ccEmail = '';
var subject = '';
var htmlBody = '<ul>';
for (Key in values) {
var label = Key;
var data = values[Key];
htmlBody += '<li>' + label + ": " + data + '</li>';
};
htmlBody += '</ul>';
MailApp.sendEmail(sendFrom, subject, htmlBody, {
htmlBody: htmlBody,
cc: ccEmail,
name: 'Automated Email',
noReply: true
});
}
Thank you for help in advance
You can access just the values (in correct order) with e.values, then dynamically create the "keys" by accessing the header row from the destination 'Form Responses' sheet:
function onFormSubmit(e) {
var values = e.values;
var vlen = values.length;
//get headers from response sheet (i.e. form questions)
var keys= SpreadsheetApp.getActive().getRange('Form Responses 1!A1:1').getValues()[0];
var i;
var htmlBody = '<ul>';
for (i=0;i<vlen;i++) {
var label = keys[i];
var data = values[i];
htmlBody += '<li>' + label + ": " + data + '</li>';
};
htmlBody += '</ul>';
/mail part etc.]
This way any edits the the questions in the form won't break your code.
A key value pair doesn't maintain order. So, you should create an array of keys to maintain the order instead, then iterate the array to retrieve each item in values.
example
var orderedKeys = ['firstName', 'lastName', 'email', 'phone']
Then in your function, instead of iterating on values, you iterate on orderedKeys instead.
for (var key of orderedKeys) {
var data = values[Key];
htmlBody += '<li>' + key + ": " + data + '</li>';
}

How do I include a header in a generated table of my javascript?

I receive in my javascript a json data that it is converted to a html table.
The script is working.. but how do I include a header in my table?
Here is the code:
<script>
$(document).ready(function(){
$('#id_adv').DataTable();
$('#id_adv').on('click','.btn',function(){
var currow = $(this).closest('tr');
var result = currow.find('td:eq(1)').text();
$.get('{% url "prop_table" %}', {var1:result}, function (data_prop) {
var data = data_prop['data_prop']
var data_json = JSON.parse(data);
var html = "";
for (var i = 0; i < data_json.length; i++) {
html += "<tr><td>" + data_json[i].fields.var1 + "</td><td>" + data_json[i].fields.var2 + "</td><tr>";
}
$('#id_prop').html(html);
})
document.getElementById('total').innerHTML = result;
});
});
</script>
Slight modification to your existing code to add a header. (I've cut out your usage of eval and just kept in the relevant part to create the HTML table header)
<script>
....
var html = "";
html += "<tr><th>Header 1</th><th>Header 2</th><tr>";
for (var i = 0; i < data_json.length; i++) {
html += "<tr><td>" + data_json[i].fields.var1 + "</td><td>" + data_json[i].fields.var2 + "</td><tr>";
}
$('#id_prop').html(html);
....
</script>
This should add a header to your table.
And I agree with the comment in your question, use the built in JSON parsing. eval is just asking to be exploited.
I think that you can add the header after create the table. The createTHead() method creates an empty element and adds it to the table. I.e.:
<script>
function createHeader() {
var table = document.getElementById("myTable");
var header = table.createTHead();
var row = header.insertRow(0);
var cell = row.insertCell(0);
cell.innerHTML = "<b>This is a table header</b>";
}
</script>

Categories