Adding to a Google Doc from a Google Spreadsheet Using Apps Script - javascript

I'm new to using the DocumentApp in Google Apps, so looking for some help!
I'm trying to create an FAQ (in a Google Doc) automatically from a spreadsheet. If certain conditions are met in the spreadsheet row, I want the script to find the category of the question in the document and insert a new question and response underneath it (pushing anything already there back).
So I'd have a document that looks like this:
https://docs.google.com/document/d/1fjb3RO6hUY6n7x0bu6WvtcleMWRNC8VQ9U82hXiGqcY/edit?usp=sharing
And a spreadsheet that looks like this:
https://docs.google.com/spreadsheets/d/1fb3ceqP6142_C7QQ1PfkWtVNswOyzOxkWCofvauf4Ps/edit?usp=sharing
And this is the code I'm trying to use. I'm getting a ton of errors- mainly because I don't have a good grasp on what the different elements involved are. Can anyone point me in the right direction?
function insertnewquestion() {
//(works fine)Get active document and related spreadsheet
var doc = DocumentApp.getActiveDocument().getBody();
var ss = SpreadsheetApp.openById("xxxxxxx");
var sheet = ss.getSheetByName("xxxxx");
//(works fine)Go through the various rows in the spreadsheet up to the last row
for (var row = 2; row <= sheet.getLastRow(); ++row) {
var status = sheet.getRange(row,4).getValue();
var question = sheet.getRange(row,1).getValue();
var response = sheet.getRange(row,2).getValue();
var category = sheet.getRange(row,3).getValue();
var date = sheet.getRange(row,5).getValue();
//(works fine)find rows with blank status and a response filled in
if (status !== "Entered" && response !== "") {
//(errors! this is where i need help) find the pertinent header
var categoryposition = body.findText(category); //looking for the category header- new text should be added after this, the idea is that what was already under this header will move down
var questionheader = categoryposition.appendText(question); //trying here to add in question text after where the category was found
questionheader.setHeading(DocumentApp.ParagraphHeading.HEADING3); //set question text as heading 3 (so it shows up on table of contents)
questionheader.appendText("\r"+"\r"+response+"\r\r"); //add line breaks and then the response text in normal font, then more line breaks to put space between new stuff and old stuff
//(works fine)Mark in the spreadsheet that it was entered in FAQ and the date so that it isn't double entered next time the script runs
sheet.getRange(row,4).setValue("Entered");
var currentTime = new Date()
var month = currentTime.getMonth() + 1
var day = currentTime.getDate()
var year = currentTime.getFullYear()
var date = (month + "/" + day + "/" + year)
sheet.getRange(row,5).setValue(date);
}
else {continue}
}
//(need help!) Refresh table of contents to include new questions
//no idea how to do this!
}

In the code you are referring to body.findText(category); where it should be doc.findText(category);. Also for the line:
var categoryposition = body.findText(category);
it returns the RangeElement — a search result indicating the position of the search text, or null if there is no match
Before adding any lines of code you have to check for null value in categoryposition.
The text class has a method to insert text in particular offset value to particular String value gives as shown here.
Hope that is helpful.

Related

Trying to Run two functions and two different emails in google scripts

I'm trying to have my sheet trigger an email alert when the last row on certain column matches an if statement after a google form feeds the sheet. it worked the first time but when i try to do a second condition it only acomplishes the second if. so separated the ifs work, but not combined.
can you guys help? here is the function:
function sendEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Observaciones Diarias IMPRG");
var lr = ss.getLastRow();
var currentTemp = ss.getRange(lr, 6).getValue();
var currentkidName = ss.getRange(lr, 3).getValue();
var currentTexture = ss.getRange(lr, 15).getValue();
if(currentTemp >39) {
MailApp.sendEmail("xxx25#gmail.com","Temperature>39°Alert", currentkidName + " had atemperature of " + currentTemp + "° " +" which is extremely high, please check")
}
else if (currentTexture = 1){
MailApp.sendEmail("xxx#gmail.com","Texture Alert", currentkidName + " had a " + " liquid Evacuation. which indicates an issue, please check")
}
}
currentTexture = 1 this is asigning a 1 to currentTexture. To do a comparison use
currentTexture == 1
or
currentTexture === 1
Related
Google Script checking that the data within one column on one sheet is the same as another column in another sheet
How do I find the cell with a certain background color, and set background of different cell in relation to it?
How to set a variable based on other variables
I'm trying to make an id searcher which does a thing when you input the right id. However, the if statement always runs. Why is this?

Creating a Google Calendar Event with Google Sheets script has Errors

I'm close, but my logic just isn't quite working. I am able to run the code below within Google Sheets Scripts to create a calendar event for each row in the sheet. I am trying to get it to only create an event when a new row is entered and to only read the spreadsheet until it comes to the first column/row being empty/null. enter image description here
Here is the associated code:
function createCalendarEvent() {
var sheet = SpreadsheetApp.getActiveSheet();
var calendar = CalendarApp.getCalendarById('dentaldigitaldesign.bkp#gmail.com');
var startRow = 100; // First row of data to process - 100 exempts my header row and adds to the case number count.
var numRows = sheet.getLastRow(); // Number of rows to process
var numColumns = sheet.getLastColumn();
var dataRange = sheet.getRange(startRow, 1, numRows-1, numColumns);
var data = dataRange.getValues();
var complete = "TRUE";
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var caseID = row[0]; //Case ID Number
var patient = row[4]; //Patient Name
var doctor = row[5]; //Doctor
var contact = row[14]; //Doctor contact info
var tooth = row[15]; //Tooth number
var shade = row[10]; //Tooth Shade
var notes = row[8]; //Notes
var callNotes = row[7]; //Call notes if there are any
var timeStamp = new Date (row[2]); //Retrieve the timestamp field
// year as 4 digits (YYYY)
var year = timeStamp.getFullYear();
// month as 2 digits (MM)
var month = ("0" + (timeStamp.getMonth() + 1)).slice(-2);
// date as 2 digits (DD)
var day = ("0" + timeStamp.getDate()).slice(-2);
var dueDate = new Date(month + day, + year); //Due date
var rDate = new Date(row[2]-1); //Remind date
var caseComplete = row[3]; //Case marked as complete
if (caseComplete !== complete && caseID !== null) {
var currentCell = sheet.getRange(startRow + i, numColumns);
calendar.createEvent(patient, rDate, rDate, {
description: doctor + '\r' + patient + '\r' + contact + '\r' + dueDate + '\r' + tooth + '\r' + shade + '\r' + notes + '\r' + callNotes
});
currentCell.setValue(complete);
}
}
}
When these events are created, they are all created for 8:59am. Ideally, I could do a check on the calendare if an event is set for that time, a new event is added immediately after. (perhaps in 15min or 30min slots). For now it works well for me to get the reminders on cases that are due, but eventually an invite to the doctor for them to know the due date might work well, too.
I can also use help in formatting the description field as it is not pushing the return value and everything is on one line.
And finally, the script continues to run on numerous fields beyond the scope of the desired rows, ultimately ending up with the script failing because too many event attempts are created in too short a time. (all the attempts with fields that are empty do not result in any events being created, but it is a resouce issue....and who knows maybe Google eventually blocks me?)
I appreciate any help that can be offered.
Here is the link to the Google Sheet. No data on it is sensitive as it is only test data: https://docs.google.com/spreadsheets/d/1M9qYzSl1PnRv-GehHEYvpiag15UI_GjnanA0wgA4xmg/edit?usp=sharing
There are several issues with your code, causing this expression to always return true, so that the script tries to create an event for each row:
if (caseComplete !== complete && caseID !== null) {
Empty string !== null:
You want to check whether the cell in column A is empty (that is, whether its value is an empty string), and to do that you are comparing its value to null. null is not the same as an empty string, so the following expression will always be true (a cell value never returns null):
caseID !== null
You should be comparing this to an empty string. For example, like this:
caseID !== ""
So the if statement should be:
if (caseComplete !== complete && caseID !== "") {
Wrong complete column:
Once an event has been created, you want to set the corresponding cell in D to TRUE. You're not doing that, since you specify the last column in the sheet (numColumns) when setting the currentCell:
var currentCell = sheet.getRange(startRow + i, numColumns);
Because of this, the value in D never changes to TRUE, so caseComplete !== complete is always true.
You should specify the correct column index for D instead, which is 4. It should be like this:
var currentCell = sheet.getRange(startRow + i, 4);
Edit: Also, the retrieved value for cell with checked checkboxes is the boolean true, and you're trying to compare it with the string TRUE. Because of this, this condition is not working correctly. Please change:
var complete = "TRUE";
To
var complete = true;
Wrong number of rows in source range:
You want to get the value from row 100 till the last one in the sheet. In this case, the total number of rows in the range should be the last row (numRows) minus the first row in the range (startRow) plus one (numRows - startRow + 1). The dataRange should be defined like this instead:
var dataRange = sheet.getRange(startRow, 1, numRows - startRow + 1, numColumns);
Reference:
Class Range
null

Extract subject and body text from Gmail messages and export to CSV

My current project is to take a set of emails from my gmail account and create a book from them, where each letter is a chapter.
I wrote up a Google Script snippet that successfully extracts the subject, date, and body text from all emails under a certain
label and writes them to a Google Document. This is not quite ideal, as I would prefer to ultimately have all this text in
markdown or LaTeX for formatting more carefully, but I was only familiar with how to to write to a Google Doc, based on the
Gmail API.
The fastest way for A to B for me would be to figure out how to alter this code so that it writes the text to a CSV file, containing
subject, date, and body columns. From there, I can process and parse it out into LaTeX and assign the necessary header styles and
formats there instead; however, I don't see much guidance on that file format in the API. Any guidance on how to adapt this script for
that purpose would be very welcome. Thanks.
function collectLetters() {
var fileName = 'OUTPUT_GOOGLE_DOC_FILENAME';
var labelName = 'MY_LABEL';
var timeZoneName = 'GMT'
var dateFormat = "dd | MM | yyyy"
var docBody;
var subject;
var date;
var body;
var message;
var num;
var datePar;
var bodyPar;
var subjectPar;
var numPar;
// get the handle for label
var label = GmailApp.getUserLabelByName(labelName);
var threads = label.getThreads();
// Create a new Google Doc
var doc = DocumentApp.create(fileName);
// get all the threads from label
for (var i = threads.length-1; i >= 0; i--) {
// EXTRACT FROM EMAIL: date, subject and body
message = threads[i].getMessages()[0];
subject = message.getSubject();
date = Utilities.formatDate(message.getDate(), timeZoneName, dateFormat)
body = message.getPlainBody().replace(/\r\n\r\n/gm,'aaaLINEBREAKERaaa').replace(/\r\n/gm,' ').replace(/ /gm,' ')
var splitBody = body.split('aaaLINEBREAKERaaa');
// APPEND TO GOOGLE DOC: Date (as Header)
datePar = doc.getBody().appendParagraph(date);
datePar.setHeading(DocumentApp.ParagraphHeading.HEADING1);
// APPEND TO GOOGLE DOC: Subject (as Subtitle)
if (subject.length > 0) {
subjectPar = doc.getBody().appendParagraph(subject);
}
else{
subjectPar = doc.getBody().appendParagraph("no subject");
}
subjectPar.setHeading(DocumentApp.ParagraphHeading.SUBTITLE);
// APPEND TO GOOGLE DOC: Body (as Normal)
docBody = doc.getBody()
splitBody.forEach(function (paragraphText) {
this.appendParagraph(paragraphText).setHeading(DocumentApp.ParagraphHeading.NORMAL);
}, docBody);
// Start a new page
doc.getBody().appendPageBreak();
//Logger.log(subject);
}
}

Values are correct and within range but still sends an email

I have a Upper Limit and Lower Limit on my spreadsheet responses that I input towards the right of my responses. The limits are both on F2 and G2. F2 is Lower Limit and G2 is Upper Limit. The code is using the function onFormSubmit and it has an MailApp.sendEmail function that will send an email in case someone enters a figure that is out of the limit.
What I do not understand that, the value that I read when the form is submitted is actually within the range but it still triggers an email.
I have tried this code specifically on a brand new form and spreadsheet and it did not have any problem.
function onFormSubmit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Responses 1");
var speedUL = sheet.getRange("K2").getValue(); //250V
var speedLL = sheet.getRange("L2").getValue(); //207V
//var speedUL= parseInt(activess.getRange("K2").getValue()); //250V
//var speedLL = parseInt(activess.getRange("L2").getValue()); //207V
var speed1 = parseInt(e.values[2]);
if((speed1 >= speedUL) || (speed1 <= speedLL)){
console.log('SpeedUL: '+speedUL);
console.log('SpeedLL: '+speedLL);
console.log('Speed1: '+speed1);
var template1 = HtmlService.createTemplateFromFile("speed1");
template1.speed1 = speed1;
MailApp.sendEmail("someone#gmail.com ",
"Out of Range Notification Speed of VSD",
"",
{htmlBody : template1.evaluate().getContent()});
}else
{
return;
}
}
This is the test script that I am trying to do. With this code, I'm getting this
Error
Aug 26, 2019, 5:24:29 PM
TypeError: Cannot call method "getRange" of null.
at onFormSubmit(Code:7)
Line 7 refers to var speedUL = sheet.getRange("K2").getValue();
Probable Cause:
getRange() is called upon spreadsheet class (ss) and not on the sheet class (sheet). This usually means value of G2 and F2 of the first sheet is taken and not the specied sheet.
e.values is array of strings. Implicit conversion takes place when numbers are compared with strings. This is probably not the cause of your issue, but still needs to be addressed.
Solution:
Call getRange() on the sheet or specify the sheet name, when calling getRange() on spreadsheet.
Use explicit type conversion of e.value to number.
Snippet:
var temperature = Number(e.values[1]);
var temperatureUCL = ss.getRange("H3 Reponses!G2").getValue();
//or
var temperatureUCL = sheet.getRange("G2").getValue();
References:
Spreadsheet#getRange
Sheet#getRange

A string of form nnnn-nnnn is displayed in a spreadsheet as a date or otherwise incorrectly

I have a script I have been using in my test environment to programmically create a tracking number by parsing the year from timestamp and padding the response index.
function setTrackingNumber(ss, lastRowInx, createDateColumn) //This block generates and stores a tracking number in Column AU on the backend
{
var padTrackNo = "" + lastRowInx;
var trackSize = 4;
var trackingNumberColumn = createDateColumn-3; //trackingNumberColumn is currently in AU (Column 47) Calculating using it's relative position to createDateColumn Position
if (ss.getRange(lastRowInx, trackingNumberColumn).getValue() == "") // so that subsequent edits to Google Form don't overwrite original tracking number
{
if (padTrackNo > trackSize)
{
var padTrackNo = pad(padTrackNo, trackSize);
}
else {} //do nothing
var shortYear = setShortYear(ss, lastRowInx, createDateColumn);
var trackingNumber = shortYear + "-" + padTrackNo;
var createTrackingNumber = ss.getRange(lastRowInx, trackingNumberColumn);
createTrackingNumber.setValue(trackingNumber);
}
else {} //should do nothing
return;
}//This is the end of the setTrackingNumber function
function setShortYear(ss, lastRowInx, createDateColumn)
{
var newCreateDate = ss.getRange(lastRowInx,createDateColumn).getValue();
var strDate = "" + newCreateDate;
var splitDate = strDate.split(" ");
var trimYear = splitDate[3];
var shortYear = trimYear;
return shortYear;
}//This is the end of the shortYear function
function pad(padTrackNo, trackSize)
{
while (padTrackNo.length < trackSize)
{
padTrackNo = "0"+padTrackNo;
}
return padTrackNo;
}//This is the end of pad function
That gets me test result which is as expected ex. 2016-0005. However when we added it to another production sheet it seemed to work with test data and then production data showed up like a date 3/1/2016. production result - first cell.
I thought it must just be formatting the string as a date because of the numbers so I tried formatted the column as plain text but that just changed the date to a plain text version of the date.
I thought this might be similar to needing to specify the format like I did in this question Appending initial timestamp from Google Form to end of record in order to permanently store create date onFormSubmit at #SandyGood 's suggestion so I tried setting the number format as [0000-0000] by changing
createTrackingNumber.setValue(trackingNumber);
to
createTrackingNumber.setValue(trackingNumber).setNumberFormat("0000-0000");
which resulted in the [production result - second cell] which again doesn't match the expected result.
Oddly, some submissions seem to work just fine like [production result - third cell]. Over the past 3 days and approximately 10 records it has been fine, then hinky, then fine, they hinky, then fine again. I am not really sure what else to try to debug this odd behaviour.
Note: I had to parse the date as a string as I was having trouble getting it to parse the date correctly from the create date which is taken from initial timestamp.
To my understanding, "2016-0005" is not a number but a string, so the cell containing it should be formatted as plain text. With a script, this can be done by
range.setNumberFormat('#STRING#')
(source), and this must be done before you set the value to the cell. Like this:
createTrackingNumber.setNumberFormat('#STRING#').setValue(trackingNumber);

Categories

Resources