Apps Script - splitting google form checkbox answers by commas - javascript

Google form checkbox question combines the selected responses into one column separated by commas. I am trying to send out a confirmation email of these responses, but the email results in a clunk format:
Register: 9:15 AM - 10:00 AM Instructional Technology Tools & Resources (electronic lab notebooks, classroom tech, analytics, etc.), 10:10 AM - 11:00 AM Tools for flipped or hybrid (blended) courses (learning glass, instructional design, Ted, podcast, etc.)
I'd like to replace the comma separator with <br> but haven't been able to figure it out.
I've tried:
register = e.namedValues[headers[4]].split(',');
register = e.namedValues[headers[4]];
workshops = register.replace(",", "<br>");
Any help would be greatly appreciated! Thank you!
for (var i in headers) {
....... .......
register = e.namedValues[headers[4]];
}
if (e.namedValues[headers[i]].toString() != "" ) {
textbody = ....... + register + "<br>"
}

Should be structured like this:
for (var i in headers) {
....... .......
register = e.namedValues[headers[i]].toString();
if (register != "" ) {
textbody = register.replace(/,/g, "<br>");
}
}
You need to perform a global replacement, after converting the array to a string.
function replaceCommas() {
var headers = ['First Name', 'Timestamp', 'Last Name', 'workshops'];
var register = {'First Name': ['Jane', 'Jon', 'Jim', 'Josie'], 'Timestamp': ['6/7/2015 20:54:13'], 'Last Name': ['Doe'], 'workshops': ['learning glass', 'instructional design', 'Ted', 'podcast']};
//register = e.namedValues[headers[4]].split(',');
var keyName = headers[3]; //Arrays are Zero indexed, fourth item is index 3
Logger.log('keyName is: ' + keyName);
var stringOfValues = register[keyName].toString();
Logger.log('stringOfValues: ' + stringOfValues);
var workshops = stringOfValues.replace(/,/g, "<br>");
Logger.log('workshops: ' + workshops);
}
Copy the above function into your script editor, set a breakpoint on something like the first Logger.log() statement, and step into each line. You can see all the values of the variables in the debug window at the bottom of the screen.
To set a breakpoint, click a line number, and a red dot will appear, that's where the code will stop. Then click the icon of the bug. The code will run up to the point of the breakpoint.
Here is a link to the debug documentation:
Google Documentation - Troubleshooting

Related

Cannot get alert to display correctly when using an if statement

I was able to get a different version of this code working with help I received here last week. I am learning so I decided to refactor it using a for loop to go through the array this time. That in itself seems to work fine until I add an 'else' to alert the user that they entered incorrect data.
I see that being inside the for loop it loops with the number of times it iterates, but no matter where I place the alert it just seems to override everything. It will pop up first no matter what is entered, then you have to click it 5 times before it displays the data in the array.
I'm open to other methods, not set on using an alert only.
I tried writing a function like this so I could just move the call around to test:
function inValidAlert(){
let cylFindInvalid =
document.getElementById("cylEnter").value.toUpperCase();
if(cylFindInvalid != cylArray.name){
let displayIt = document.getElementById("displayCyl");
displayIt.innerHTML = displayIt.innerHTML + "Enter a valid
cylinder type";
}
}
I placed invalidAlert() inside the listCylinders function, outside the function, etc...That gave me the same results as just writing an alert, but gave me some practice anyway...
This might seem like a no brainer to some of you, but I am learning Javascript and need help to figure this one out:)
The code shown works without the alert statement. So a user enters a cylinder type with the 'name' property of one in the array. LD, RD, GD, etc...
If it is valid then the data in the object (in the array) displays on the screen.
But if an invalid entry is seen, I want a pop up alert to show that says "Invalid Data" or similar. The pop up will work but at the wrong time or taking multiple clicks to clear it if inside the for loop. If I use a 'break' then the alert overrides the entire if statement and will fire no matter what is entered.
So how would I go about getting the alert to fire properly? Maybe the for loop is the wrong overall approach to begin with? Let me know if I need to post my HTML also. I am new and trying to learn the ropes here, please go easy on me.
function listCylinders() {
let display = document.getElementById("displayCyl");
for (var i = 0; i < cylArray.length; i++) {
let cylCompare = document.getElementById("cylEnter").value.toUpperCase();
if (cylCompare == cylArray[i].name) {
display.innerHTML = display.innerHTML + cylArray[i].name + ":" + "<br>" + cylArray[i].brand +
"<br>" + cylArray[i].cylinder + "<br>" + cylArray[i].pins + "<br>" + cylArray[i].type;
} else {
alert("Enter a valid cylinder type");
}
}
}
//function used to disable the button after it is used once.
const setbutton = document.getElementById('button1');
setbutton.addEventListener('click', disableButton);
function disableButton() { 
setbutton.disabled = true;
}
//function that will clear the form as well as the display when clicked.
function clearCyl() {
var cylDisplay = document.getElementById("displayCyl");
document.getElementById("cylForm").reset();
cylDisplay.innerHTML = "";
setbutton.disabled = false;
}
//cylinder type properties shown as individual objects inside of an array.
var cylArray = [{
name: 'LD',
brand: 'Schlage, Falcon',
cylinder: ' Without cylinder',
pins: ' 6 Pin',
type: ' KIL'
},
{
name: 'RD',
brand: 'Schlage-Everest 29 S123 (Standard)',
cylinder: ' With cylinder',
pins: ' 6 Pin',
type: ' FSIC'
},
{
name: 'PD',
brand: 'Schlage-Everest 29 S123 (Standard)',
cylinder: ' With cylinder',
pins: ' 6 Pin',
type: ' KIL'
},
{
name: 'JD',
brand: 'Schlage',
cylinder: ' Without cylinder',
pins: ' 6 Pin',
type: ' FSIC'
},
{
name: 'GD',
brand: 'Schlage-Everest 29 R Family keyways',
cylinder: ' With cylinder',
pins: ' 7 Pin',
type: ' SFIC'
}
];
In your loop logically it will always alert at least 5 times. This is because cylCompare never changes, so it will never be equal to the other 5 .name properties in the array you loop. So cylCompare == cylArray[i].name can only ever be true for one of your six iterations. Hence the alert showing 5 times in a row.
Instead what you will want to do is if the user input never matches a single item in your array, then you will want to say it's invalid. You can do this by assuming it's invalid first, then once you find any match change it to being valid:
function listCylinders() {
let display = document.getElementById("displayCyl");
let valid = false; // We assume this isn't valid
// You can move this outside of the loop, and trim in case the user entered spaces
let cylCompare = document.getElementById("cylEnter").value.toUpperCase().trim();
for (var i = 0; i < cylArray.length; i++) {
if (cylCompare == cylArray[i].name) {
valid = true; // We find a match in one of our array items, so it's valid
display.innerHTML = display.innerHTML + cylArray[i].name + ":" + "<br>" + cylArray[i].brand +
"<br>" + cylArray[i].cylinder + "<br>" + cylArray[i].pins + "<br>" + cylArray[i].type;
}
}
if(!valid) alert("Enter a valid cylinder type"); // If we never found a match then we alert
}

Email multiple rows to one recipient based on category values using Apps Script

What I'm looking for:
I am working on a spreadsheet that contains transaction information for multiple people. Some people have only 1 transaction (1 row of information) while others may have multiple transactions (rows of information). So far, the code I am working on is able send an email containing one transaction per email. However, I am having trouble figuring out how to group multiple transactions into one email. One of the column fields is "Email address" and ideally I could use that to group multiple rows. Pretty much a mail merge that can be grouped by a column in the spread sheet or by the identification of similar values.
What I've tried/what I'm thinking:
I already have the code sort by email address so that the transactions for each person are consecutive on the sheet. I am thinking that I need to loop with an if statement saying something along the lines "if the next value in the 'email address' field is the same as the previous one, add a new line in the body of the email." From what I have researched I might need to do a loop within a loop for the email body but I am not completely sure how to go about that. In the code I have, I have already somewhat formatted the email the way it should be.
Disclaimer:
I'm pretty new to coding so any help is appreciated. I am unsure how to loop in the body of the message and eventually exit once the script hits a different person. I'd like to do it all in GAS.
This is what I've got so far:
var SENT = "Y";
function sendEmails() {
//This section specifies the sheet and some definitions we are working with
var sheet =SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var lastrow = sheet.getLastRow();
var startrow = 4; // First row of data to process
var numrows = lastrow; // Number of rows to process
sheet.sort(14, true); //Sorts the sheet by email address
//This section formats the columns so they appear correctly in the html email below
var column = sheet.getRange("K4:L");
column.setNumberFormat("MM/dd/yyyy"); // Simple date format
var column = sheet.getRange("M:M");
column.setNumberFormat("#"); // Simple plain text format
var column = sheet.getRange("K4:L");
column.setNumberFormat("#"); // Simple plain text format
var column = sheet.getRange("E:E");
column.setNumberFormat("$0.00"); // Simple currency format
//This section specifies the actual data we will be working with
var datarange = sheet.getRange(startrow, 1, lastrow, 15)// Fetch the range of cells
var data = datarange.getValues();// Fetch values for each row in the Range.
//Defining column data
for (var i = 0; i < data.length; ++i) {
var col = data[i];
var firstname = col[2]; // Column starting at 0 from left to right
var expamount = col[4]; // Column starting at 0 from left to right
var subdate = col[10]; // Column starting at 0 from left to right
var trandate = col[11]; // Column starting at 0 from left to right
var reportname = col[12]; // Column starting at 0 from left to right
var emailaddress = col[13]; // Column starting at 0 from left to right
var emailsent = col[14]; //Column starting at 0 from left to right
var subject = "MinistryCard Expenses Over 90 Days"; // Subject for the email to be sent
var emailintro = // Introduction part of the email
'Hi ' + firstname + ', <br /><br />' +
'This is the introduction to the email.' +
'The related expenses are below: <br /><br/ >'
var emailtrans = //THIS IS WHERE I WOULD LIKE THE MAGIC TO HAPPEN... I THINK
'<strong>Report Name: </strong>' + reportname + ' <br /><strong>Transaction Date: </strong>' + trandate + ' <br /><strong>Transaction Amount: </strong>' + expamount +
' <br /><strong>Approval Date: </strong>' + subdate + '<br /><br />'
var emailend = // The endd of the email
'We apologize for any inconvenience this may cause. ' +
'Also, please contact us if you believe you are recieving this email in error. Thank you. <br /><br /><br />' +
'Enjoy your day, <br /><br />'
//The section below retrieves alias email address to send as
var me = Session.getActiveUser().getEmail();// Log the aliases for this Gmail account and send an email as the first one.
var aliases = GmailApp.getAliases(); // Gets the alias ministrycard#cru.org from account
Logger.log(aliases); // Logs the alias
// The section below sends the actual emails
if (emailaddress != "" && emailsent != SENT){
if (aliases.length > 0){ // Prevents sending duplicates
GmailApp.sendEmail(emailaddress, subject , emailintro + emailtrans + emailend, {'from': aliases[0],'replyto': 'ministrycard#cru.org', htmlBody : emailintro + emailtrans + emailend});
sheet.getRange(startrow + i, 15).setValue(SENT);
SpreadsheetApp.flush(); // Make sure the cell is updated right away in case the script is interrupted
}} else { }}}
There are multiple solutions. Here's mine:
declare a nextEmail variable before the main for loop begins.
var nextEmail = '';
at the beginning of the loop, set nextEmail to the email in the next row:
if (i < data.length - 1) {
nextEmail = data[i+1][13]//looks at next row, 13th column
} else {//catch if i is the maximum row
nextEmail = ''
}
add another conditional that checks that the next email address is not the same before sending the email
if (emailaddress != "" && emailsent != SENT && nextEmail != emailaddress){
You didn't seem to specify what parts of the message you want to change each time and what parts you want to stay the same, but I'm assuming you want to change only emailtrans.
now, instead of declaing a new variable emailtrans each time it loops around, declare emailtrans before the loop as an empty string...
var emailtrans = ''
...and instead append the big string to emailtrans
emailtrans +=
'<strong>Report Name: </strong>' + reportname + ' <br /><strong>Transaction Date: </strong>' + trandate + ' <br /><strong>Transaction Amount: </strong>' + expamount +
' <br /><strong>Approval Date: </strong>' + subdate + '<br /><br />';
and then set emailtrans to an empty string right after you send the email, inside of the if statement.
emailtrans = ''
There are a few things you didn't ask about but I still think should be addressed. You probably would have caught a lot of this if only you tested whatever you've got, even if it's incomplete, to make sure it did what you want before asking for help.
SENT variable should probably be declared within the function (good practice)
numrows is only equal to lastrow if startrow is zero. Set numrows to lastrow - startrow and use numrows instead of lastrow when you declare datarange.
You sort the whole sheet based on column M, but your script only collect data from row 4 onward. Depending on your labels in rows 1-3, the sorting might place some emails in rows 1-3 and your labels in row 4 and onward. Instead, I'm guessing you only want to sort the range containing the emails, so do a .sort() on that range instead of the whole sheet.
Putting in semicolons where you define emailintro, emailtrans, and emailend in multiple lines is probably a good idea.
I could be wrong, but it seems the html tags aren't formatted right. <br /><br /> wouldn't insert a break ... they would just end two breaks?
Declaring a variable inside a for loop is bad practice. Declare it outside the loop beforehand, and set it (thing = value), not declare it (var thing = value), inside the loop.
Hope this helps.

search command ignores special characters

What I'm trying to do: I'm trying to parse Google Calendar events and create a Google Document if the following string is found in the title or description of the event "#notes".
What actually happens: A document is created as soon as the string "notes" is found. It doesn't require an hashtag. The same thing happens with #.
function createMeetingNotesNextTimePeriod() {
//<-------------------------------------VARIABLES
// get today's date
var today = new Date();
// number of hours from now to check for meetings
var hours = 2
// create variable for now
var now = new Date();
// create variable for number of hours from now in milliseconds
var period_from_now = new Date(now.getTime() + (hours * 60 * 60 * 1000));
// retrieve all calendar events for time period with #notes string
var tagword = ('#notes');
var events = CalendarApp.getDefaultCalendar().getEvents(now, period_from_now, {search: tagword}) ;
Logger.log('Number of events: ' + events.length);
} // functionCreateMeetingNotesNextTimePeriod
The log commands tells me that it found one event but the event in question just has 'notes' in it's title, no hashtag. Thanks for all help.
EDIT
I think I found the problem but I'm still stumped about solving it. It seems that the search option from .getEvents ignores most special characters and returns a positive whenever you search for a special character. For example, I replaced my original search for tagword with this:
var events = CalendarApp.getDefaultCalendar().getEvents(now, period_from_now, {search: 'notes' + '#' +'#' + '±' }) ;
It gives me a positive match on my search while I don't have either a '#', a '#' nor a '±' in my event.
That does seem odd that it ignores special characters however here is a work around that should work:
var tagword = '#notes';
var events = CalendarApp.getDefaultCalendar().getEvents(now, period_from_now, {search : tagword})
if (events.length > 0) {
for (i in events) {
if ( events[i].getTitle().indexOf(tagword) >-1 || events[i].getDescription().indexOf(tagword) >-1) {
//do something
}
}
}
Basically it will find all events that have notes (ignoring the special characters) then loop through the event titles and descriptions to check if the tag word is present (including special characters).
You will need to use some Regex to do what you are trying to do in Javascript.
I am not exactly sure how to do it for your particular case but here are two references that can help you out.
JS Extract HashTags From Text
JS Regex Searching for HashTags
I hope this helps !
EDIT
try
var tagword = ('/#/g' + notes);
OR
var tagword = ('/#notes/g');
I tried it out and it seems to have worked.

Pebble SDK/SimplyJS not honoring the \t character

I'm having an interesting time trying to get my Pebble watch to honor the escape sequence character \t when pushing data to my watch (using SimplyJS).
The following snippet is the code that I have been using:
simply.scrollable(true);
simply.style('small');
simply.fullscreen(true);
var aflLadderUrl = 'http://www.sportal.com.au/feeds/sss/afl_ladder.json';
var ladderContent = '';
ajax({ url: aflLadderUrl, type: 'json'},
function(data) {
var i = 0;
while (i < 18){
ladderContent = ladderContent + data.ladder[i].friendly_name + '\t\t\t' + data.ladder[i].percentage + '\n';
i++;
}
simply.text({
title: 'AFL Ladder',
body: ladderContent
});
},
function() {
simply.text({
title: 'AFL Ladder',
body: 'No internet connection'
});
}
);
What I am currently observing is that the \n is being honored and I can see that on my watch, each row of data is being displayed on a separate line. However, it appears that my \t are being ignored and instead of a tab being inserted into my line, zero whitespace is being displayed (i.e. 'my name is' + '\t\t\t' + 'dave' is displayed as my name isdave).
I have also tried compiling a Hello World program using just the Pebble SDK (taking the code from https://github.com/kristofvc/pebble-hello-world/blob/master/src/pebble_hello_world.c and adding a couple of \t\t in the string to be printed on line 11) and have also noticed that the SDK honors \n characters but not \t characters (just as my SimplyJS app did).
My question is: is it possible to have the Pebble (either via the SDK or SimplyJS) display tabs the same way you'd expect them to work when printing to console? I understand the \t character may not be supported and instead of using \t I could just use spaces, but this one had me curious.
Please let me know if you require any more information.
Thanks in advance!
Because \t is a control (non-printable) character, it doesn't correspond to any glyph and won't get "displayed" consistently, if at all. Terminal emulators will interpret it differently than spreadsheet software reading a TSV file, for example. It sounds like the part of the Pebble's firmware that converts a bytestring to pixels on a display just ignores \t, although I couldn't find this in the SDK documentation.
If you're using a fixed-width font, you can implement your own tabs using spaces, like this:
var data = {ladder: [
{friendly_name: "Cool Team", percentage: 80.0},
{friendly_name: "Really Cool Team", percentage: 80.0},
{friendly_name: "The Coolest Team of All", percentage: 80.0}
]}
var len = function(obj) {return obj.friendly_name.length}
var longer = function(a, b) {return len(a) > len(b) ? a : b}
var longestName = len(data.ladder.reduce(longer))
var tab = function(obj) { return new Array(longestName - len(obj) + 2).join(" ") }
var print = function(obj) { return obj.friendly_name + tab(obj) + obj.percentage }
var ladderContent = data.ladder.map(print).join("\n")
// Cool Team 80
// Really Cool Team 80
// The Coolest Team of All 80

Google Apps Script - Dynamically Add Remove UiApp Form Elements

I am looking to create a Ui form section in my application that will Dynamically Add Remove UiApp Form Elements. I was trying to use the example from App Script Tutorials here
This example works great as far as performing the add remove elements, but when I use the submit button to capture the values, it submits as a JSON.stringify format. When I just want to capture the values only in a text or string format that will be added to a html email.
If there is way to convert JSON.stringify to text, string or get the values only in format, I will continue to use this example.
If not I was wonder if the following Javascript HTML code can be convert into GAS code and able to capture the values for each entry in a HTML email template to using in MailApp.
http://jsfiddle.net/g59K7/
Any suggestions, examples or adjustments to the codes would be greatly appreciated.
Thank you in advance
If you don't want the result to be in a JSON object, then you can adjust the _processSubmittedData(e) function. Right now he has it writing everything to an Object, which is fine. All you have to do is have a way to parse it:
function _processSubmittedData(e){
var result = {};
result.groupName = e.parameter.groupName;
var numMembers = parseInt(e.parameter.table_tag);
result.members = [];
//Member info array
for(var i=1; i<=numMembers; i++){
var member = {};
member.firstName = e.parameter['fName'+i];
member.lastName = e.parameter['lName'+i];
member.dateOfBirth = e.parameter['dob'+i];
member.note = e.parameter['note'+i];
result.members.push(member);
}
var htmlBody = 'Group Name: ' + result.groupName;
for(var a in result.members) {
var member = result.members[a];
var date = member.dateOfBirth;
var last = member.lastName;
var first = member.firstName;
var note = member.note;
htmlBody += first + ' ' + last + ' was born on ' + date + ' and has this note: ' + note;
}
MailApp.sendEmail('fakeEmail#fake.com',"Test Subject Line", "", {htmlBody: htmlBody});
}

Categories

Resources