I am quite new the using Google Apps Script, but not to JavaScript. I was wondering if there was a way that I could retrieve some text from a Google Docs, and send it in an email, something like this.
This is not the correct syntax, but bear with me.
function createAndSendDocument() {
var email = "email";
var subject = "Subject";
var body =
GmailApp.sendEmail(email, subject, body);
}
Is there a way that I could do something like this,
var randnum = random(1,4)
getText(documentname, text from document)
Text on the document would look something like this.
Hello
Test
How are you?
Watermelon!
Where getting the text from the document is based on randnum, example: randnum = 1 so email body would be "Hello"
Sorry for all the jumping around in the question, but hope you can answer this. Comment if you need any clarification.
I believe I understand what you are looking for.
The tricky part is understanding how you have formatted the list in the Google Doc. A list can be formatted as an actual indented list where the list items are automatically incremented as you enter a new line or a list can just be a hardcoded list of entries where nothing is automatically indented and the list items are manually incremented. See the two examples in this document. Accessing the lines is different depending on how the list is built. If you truly only care about finding a particular numbered child, I would probably suggest formatting it as an actual list. If you think you might require more advanced searching, I would format it as a hardcoded list, because then you can use some regular expressions to find the relevant child.
Now to actually grabbing the text, this is what I came up with (please note that this is written in the new-ish V8 engine, so you won't be able to use this in the legacy script editor):
const getTextFromDocument = (documentID, randomNumber) => {
const doc = DocumentApp.openById(documentID);
const body = doc.getBody();
// get text from non-list elements
const find = body.findText('^' + randomNumber);
if (find) {
return find.getElement().asText().getText();
}
// get text from list elements
const listItems = body.getListItems();
for (let lix = 0; lix < listItems.length; lix++) {
const listItem = listItems[lix];
if (lix + 1 === randomNumber) {
return listItem.getText();
}
}
}
If the script finds the information in a hardcoded list, the first chunk runs, otherwise it'll proceed into the next chunk.
Depending on how the text is formatted, the returned text is slightly different. If the text is formatted as an actual list, the text looks like this:
Watermelon!
(Without the number.) Whereas if it's a hardcoded listed, the returned text looks like this:
4. Watermelon!
(With the preceding number.)
With that, sending the email will look something like this:
const sendEmail = () => {
const documentID = '1DljXJtDdZLKr_TcZKuFESo4VrgPEpwXLI3bM3RA0Kjk';
const randomNumber = Math.floor(Math.random() * 4) + 1;
const text = getTextFromDocument(documentID, randomNumber);
console.log(randomNumber);
console.log(text);
// MailApp.sendEmail({
// to: 'someone#gmail.com',
// subject: 'Subject Line',
// body: text
// });
}
I opted for MailApp as opposed to GmailApp, since MailApp is meant for sending emails exclusively whereas GmailApp also offers methods for managing Gmail. You can test it out over here if you want. If you want to take advantage of more email styling, I recommend checking out the guides over here. And if you want to read more about DocumentApp, you can check that out here.
Related
OK Ive been able to get the following to partially work
var Global_Wound_array =[{"WoundNumber":1,"BodySide":"Front","BodyPart":"Nose"},{"WoundNumber":2,"BodySide":"Left","BodyPart":"Head"},{"WoundNumber":3,"BodySide":"Back","BodyPart":"Ear"}]
var Global_Wound_Counter = 1
I can get the page to loop through and display the individual wounds but I need a way to say at a particular page one of the values eg on WoundNumber 2 BodyPart has changed and updated the string without affecting the rest of it.
page9200.setEventHandler("pageFinishing", function () {
//getSelectedButtonLabel this is ok - specific on the system
let Q1 = Q3_WoundNumber.getValue();
let Q2 = Q1_BodySide.getSelectedButtonLabel();
let Q3 = Q2_BodyPart.getSelectedButtonLabel();
for (var i = 0; i < Global_Wound_array.length; i++) {
if (i+1 == Q1){
//create new temp variable array
var Temp_Wound_obj2 = {"WoundNumber": Q1,"BodySide": Q2,"BodyPart":Q3}
Global_Wound_array.push(Temp_Wound_obj2)
}
}
});
As well as being able to reach the end of the string to present a blank set of values to have the option to add a new wound.
Every time I think Ive got something that looks like it would work I go around in circles, when I try to update the system at the end I get and error that the - invaid parameters for RPC call: variable is bad
It seems you are pasting JSON onto JSON, with no separator. This creates a messy and non-standard data structure. If you wrote your JSON with a newline at the end, you would end up with a JSONL file, which is very simple to process.
const jsonl = `
[{"WCount":1,"Side":"Centre","Part":"Ocipit","Type":"Other","SurroundingSkin":"Dermatitis","Height":"","Width":"","Depth":""}]
[{"WCount":2,"Side":"Front","Part":"Neck","Type":"Diabetic foot wound","SurroundingSkin":"Healthy/intact","Height":"3","Width":"4","Depth":"5"}]
`;
const jsonItems = jsonl.trim().split("\n");
const lastJsonItem = jsonItems[jsonItems.length - 1];
const lastItem = JSON.parse(lastJsonItem);
const lastWCount = lastItem[0].WCount;
console.log(lastWCount);
If you already have a file without newlines... it would be best to insert them, and correct your data to JSONL. This is simple in your case just by replacing ][ with ]\n[ (and making sure the file ends with a newline too, so the next write would not be messed up), since you have no nesting and (hopefully) no ][ in your text, but in general it is not easy - I don't know of a JSON parser that will return unconsumed text, so it would probably involve writing a JSON parser. Much easier to write data correctly in the first place.
I have a Google Sheet with .gs script that is successfully generating dynamicnewRichTextValue() parameters which are meant to be injected into a Sheet cell that will contain multiple lines of text each with their own URL. I do not know all of the parameters in advance (might be one text and one link, or two each, or more) which is why I am dynamically generating the parameters.
Let's say the end-state should be this (in this case there are only two line items, but there could be more or less:
var RichTextValue=SpreadsheetApp.newRichTextValue()
.setText("mailto:fred#abcdef.com,mailto:jim#abcdef.com")
.setLinkUrl(0,6,"mailto:fred#abcdef.com")
.setLinkUrl(7,19,"mailto:jim#abcdef.com")
.build();
In my script I don't know how many "setText" parameters or "setLinkUrl" statements I will need to generate, so I am doing it dynamically.
This is simple to handle for "setText" because I can just pass a single variable constructed during an earlier loop that builds the "setText" parameters. Let's call that variable setTextContent, and it works like this:
var RichTextValue=SpreadsheetApp.newRichTextValue()
.setText(setTextContent)
So up to this point, everything is great. The problem is that I have another variable that generates the URL portion of the newrichtextvalue() parameters up to the ".build();" statement. So let's call that variable setUrlContent and it is built in an earlier loop and contains the string for the rest of the statement:
.setLinkURL(0,22,"mailto:fred#abcdef.com").setLinkURL(23,44,"mailto:jim#abcdef.com")
I am stumped trying to figure out how to attach it to the earlier bit. I feel like this is something simple I am forgetting. But I can't find it after much research. How do I hook up setUrlContent to the code above so that the command executes? I want to attach the bits above and get back to assigning it all to a variable I can put into a cell:
var emailCell=SpreadsheetApp.newRichTextValue()
.setText("mailto:fred#abcdef.com,mailto:jim#abcdef.com") // I can dynamically create up to here
.setLinkUrl(0,6,"mailto:fred#abcdef.com") // ...but these last couple lines are
.setLinkUrl(7,19,"mailto:jim#abcdef.com") // stuck in a string variable.
.build();
sheet.getRange(1,1,1,1).setRichTextValue(emailCell)
Thanks!
I believe your goal and situation as follows.
You want to use your script by dynamically changing the number of emails.
Modification points:
When your following script is run, I think that the links are reflected to mailto and fred#abcdef..
var emailCell=SpreadsheetApp.newRichTextValue()
.setText("mailto:fred#abcdef.com,mailto:jim#abcdef.com")
.setLinkUrl(0,6,"mailto:fred#abcdef.com")
.setLinkUrl(7,19,"mailto:jim#abcdef.com")
.build();
sheet.getRange(1,1,1,1).setRichTextValue(emailCell)
I thought that you might have wanted the linked email addresses like below.
fred#abcdef.com has the link of mailto:fred#abcdef.com.
jim#abcdef.com has the link of mailto:jim#abcdef.com.
In this answer, I would like to propose the modified script for above direction.
Modified script:
var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com"; // This is your sample text value.
var ar = inputText.split(",").map(e => {
var v = e.trim();
return [v.split(":")[1], v];
});
var text = ar.map(([e]) => e).join(",");
var emailCell = SpreadsheetApp.newRichTextValue().setText(text);
var start = 0;
ar.forEach(([t, u], i) => {
var len = t.length;
emailCell.setLinkUrl(start, start + len, u);
start += len + 1;
});
SpreadsheetApp.getActiveSheet().getRange(1,1,1,1).setRichTextValue(emailCell.build());
In this modification, inputText is splitted to the hyperlink and the text (for example, when your sample value is used, it's fred#abcdef.com and mailto:fred#abcdef.com.), and the text including the hyperlink are put to the cell.
In this case, for example, even when var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com" is modified to var inputText = "mailto:fred#abcdef.com" and var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com,mailto:sample#abcdef.com", each hyperlink are reflected to each text.
Note:
When you want to the hyperlink of mailto:fred#abcdef.com to the text of mailto:fred#abcdef.com, you can also use the following modified script.
var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com"; // This is your sample text value.
var ar = inputText.split(",").map(e => e.trim());
var emailCell = SpreadsheetApp.newRichTextValue().setText(inputText);
var start = 0;
ar.forEach((t, i) => {
var len = t.length;
emailCell.setLinkUrl(start, start + len, t);
start += len + 1;
});
SpreadsheetApp.getActiveSheet().getRange(1,1,1,1).setRichTextValue(emailCell.build());
References:
newRichTextValue()
Class RichTextValueBuilder
Class RichTextValue
I have some text stored in a database, which looks something like below:
let text = "<p>Some people live so much in the future they they lose touch with reality.</p><p>They don't just <strong>lose touch</strong> with reality, they get obsessed with the future.</p>"
The text can have many paragraphs and HTML tags.
Now, I also have a phrase:
let phrase = 'lose touch'
What I want to do is search for the phrase in text, and return the complete sentence containing the phrase in strong tag.
In the above example, even though the first para also contains the phrase 'lose touch', it should return the second sentence because it is in the second sentence that the phrase is inside strong tag. The result will be:
They don't just <strong>lose touch</strong> with reality, they get obsessed with the future.
On the client-side, I could create a DOM tree with this HTML text, convert it into an array and search through each item in the array, but in NodeJS document is not available, so this is basically just plain text with HTML tags. How do I go about finding the right sentence in this blob of text?
I think this might help you.
No need to involve DOM in this if I understood the problem correctly.
This solution would work even if the p or strong tags have attributes in them.
And if you want to search for tags other than p, simply update the regex for it and it should work.
const search_phrase = "lose touch";
const strong_regex = new RegExp(`<\s*strong[^>]*>${search_phrase}<\s*/\s*strong>`, "g");
const paragraph_regex = new RegExp("<\s*p[^>]*>(.*?)<\s*/\s*p>", "g");
const text = "<p>Some people live so much in the future they they lose touch with reality.</p><p>They don't just <strong>lose touch</strong> with reality, they get obsessed with the future.</p>";
const paragraphs = text.match(paragraph_regex);
if (paragraphs && paragraphs.length) {
const paragraphs_with_strong_text = paragraphs.filter(paragraph => {
return strong_regex.test(paragraph);
});
console.log(paragraphs_with_strong_text);
// prints [ '<p>They don\'t just <strong>lose touch</strong> with reality, they get obsessed with the future.</p>' ]
}
P.S. The code is not optimised, you can change it as per the requirement in your application.
There is cheerio which is something like server-side jQuery. So you can get your page as text, build DOM, and search inside of it.
first you could var arr = text.split("<p>") in order to be able to work with each sentence individually
then you could loop through your array and search for your phrase inside strong tags
for(var i = 0; i<arr.length;i++){
if(arr[i].search("<strong>"+phrase+"</strong>")!=-1){
console.log("<p>"+arr[i]);
//arr[i] is the the entire sentence containing phrase inside strong tags minus "<p>"
} }
I wrote a Google Apps Script that pulls a single spreadsheet cell from new Google Form entries and sends its contents via email.
The script is working nicely, but the cell content is sent as a single block of text (newlines, paragraphs, etc are dropped). The Google Form entry is of "Paragraph/Long Text" type and I'd like to maintain the authors' formatting in the generated email.
I am tinkering with string types, but can't quite find the right combination. Any advice will be immensely appreciated. Code below.
function SendEmail() {
// find out how many rows exist
var numRows = SpreadsheetApp.getActiveSheet().getRange("A:A").getLastRow();
// fetch entry
var messageRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Response").getRange("D" + numRows);
// build message
var message = {
to: "destination#email.net",
subject: "New Entry Posted",
htmlBody: 'Hello, a new entry was posted.<p> <p>' + messageRange.getValues() + "<p> <p>Link here."
};
// send
MailApp.sendEmail(message);
}
Keeping Line-Breaks
For preserving line-breaks, the issue is text based line breaks (carriage return, \r, or newline, \n) are ignored in HTML. The best ways to create spacing between lines is to either use the dedicated "line break" element, or separate text into individual elements and add spacing with CSS.
Luckily, converting Google Sheets line breaks into HTML is really easy. Simply change messageRange.getValue() to messageRange.getValue().replace(/[\r\n]{1,2}/g,"<br>").
Advanced Formatting
If you are looking to preserver more advanced formatting, such as colors and images, the solution gets a bit trickier. Both range.getValue() and event range.getRichTextValue().getText() both return plain text. In order to convert to HTML, you need to use a bunch of other methods, like range.getFontColors();, and parse the output into CSS and combine it with the plain text value. There is a dedicated library called SheetConverter to accomplish this, and you can see this SO answer for details.
Other things I noticed:
I noticed a few other things about your code you might want to change. You might have noticed in my solution that I used messageRange.getValue(), but in your code you have messageRange.getValues(). This is because you want a single value, but range.getValues() is for getting multiple values out of a range of > 1 cells.
You also have malformed HTML in your htmlBody. You open a bunch of <p> tags, but never close them with </p>.
Another thing is that the way you get the last row doesn't really make sense. You check the last row of "A:A" in the active sheet, but the active sheet can change and there is no guarantee that it is the same as "Form Response", or that A:A has the same last row as D:D. I think a safer solution would be something like this (which also includes the above recommended changes):
function SendEmail() {
var formResponseSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Response");
// fetch entry
var messageRange = formResponseSheet.getRange("D" + formResponseSheet.getLastRow());
// build message
var message = {
to: "destination#email.net",
subject: "New Entry Posted",
htmlBody: 'Hello, a new entry was posted.<p>' + messageRange.getValue().replace(/[\r\n]{1,2}/g,"<br>") + "</p>Link here."
};
// send
MailApp.sendEmail(message);
}
The context: I need to process/correct many text documents containing multiple particular textual errors, highlight keywords in 'bold' and then output the result. I have a Google spreadsheet with two worksheets: one with two columns of 'wrong wordforms' and 'replacement wordforms' (2d array) that I intend to add to over time and use it as a datastore to 'call from;' the other, a single-column collection of words (1d array) I designate "keywords" to check for and then highlight in the target documents.
Things I've tried that worked: I used a basic array iteration loop from a beginner video (I can't add more links yet, I apologize) and swapped in body.replaceText() for the sendEmail(), successfully, to process the corrections from my "datastore" into my target document, which works nearly perfectly. It ignores text values without the exact same case...but that's a problem for another day.
function fixWords() {
// Document to edit
var td = DocumentApp.openById('docId1');
// Document holding comparison datastore
var ss = SpreadsheetApp.openById('docId2');
// Create data objects
var body = td.getBody();
var sheet = ss.getSheetByName("Word Replacements");
var range = sheet.getDataRange();
var values = range.getValues();
// Create a loop (iterate through the cell data)
for (i=1;i<values.length;i++) {
fault = values[i][0];
solution = values[i][2];
body.replaceText(fault, solution);
}
}
Things I've tried that fail: I then tried just swapping out values for setBold() with the replaceText() code, but the closest I got was the first instance of a keyword from the array would be styled correctly, but no further instances of it...unlike ALL of the instances of an incorrectly spelled word being corrected from the Word Replacements array using the fixWords function.
I found the 'highlightTextTwo' example here at stackoverflow which works very well, but I couldn't figure out how to swap in an external data source or force the included different iteration loop to work in my favor.
I've scanned the GAS reference, watched Google developer videos for snippets that might apply...but clearly I'm missing something that's probably basic to programming. But I honestly don't know why this isn't as easy as the body.replaceText() functionality.
function boldKeywords() { // https://stackoverflow.com/questions/12064972
// Document to edit
var doc = DocumentApp.openById('docId1');
// Access the keyword worksheet, create objects
var ss = SpreadsheetApp.openById('docId2');
var sheet = ss.getSheetByName("Keywords");
var range = sheet.getDataRange();
var values = range.getValues();
var highlightStyle = {};
highlightStyle[DocumentApp.Attribute.BOLD] = 'true';
for (i=1; i<values.length; ++i) {
textLocation = values[i];
if (textLocation != null && textLocation.getStartOffset() != -1) {
textLocation.getElement().setAttributes(textLocation.getStartOffset(),textLocation.getEndOffsetInclusive(), highlightStyle);
}
}
}
This throws out 'TypeError: Cannot find function getStartOffset in object DIV. (line 15, file "boldIt").' I guess this means that by just blindly swapping in this code, it's looking in the wrong object...but I cannot figure out why it works for x.replaceText() and not for x.setAttributes() or x.setBold or .getElement().getText().editAsText()...there just doesn't seem to be a "Learn Google Apps Script example" that deals with something this low on a scale of mundane, uninteresting use cases...enough for me to figure out how to direct it to the right object, and then manipulate the "if statement" parameters to get the behavior I need.
My current brick wall: I spotted this example, again, Text formatting for strings in Google Documents from Google Apps Script, and it seemed promising, even though the DocsList syntax has been deprecated (I'm fairly sure). But now I get "bold is not defined" thrown at me. Bold...is not defined. :: mouth agape ::
function boldKeywords() {
// Access the keyword worksheet, create objects
var ss = SpreadsheetApp.openById('docId1');
var sheet = ss.getSheetByName("Keyterms");
var range = sheet.getDataRange();
var values = range.getValues();
// Open target document for editing
var doc = DocumentApp.openById('docId2');
var body = doc.getBody();
// Loop function: find given keyword value from spreadsheet in target document
// and then bold it (highlight with style 'bold')
for (i=1; i<values.length; ++i) {
keyword = values[i];
target = body.findText(keyword);
body.replaceText(target,keyword);
text = body.editAsText();
text.setBold(text.startOffset, text.endOffsetInclusive, bold);
}
}
I will happily sacrifice my firstborn so that your crops may flourish for the coming year in exchange for some insight.
I use this for my scripts, the setStyleAttribute method.
Documentation : https://developers.google.com/apps-script/ui_supportedStyles
Example :
TexBox.setStyleAttribute("fontWeight", "bold");
The bold parameter is a Boolean data type. You need to use the word true or false.
Replace "bold" with "true".
text.setBold(text.startOffset, text.endOffsetInclusive, true);
Check out the "Type" column in the documentation:
Google Documentation - setBold