I should preface by saying I know nothing about scripting. I found this script online that fit my needs, so I was able to re-purpose it for my project. Anyway, this script takes Google form submissions, populates a Google doc template, that template gets copied, converted to PDF, and placed in a specific folder on my Google Drive.
So my question is, I have a simple line that pulls the current date when the script gets run, but I also need some code that can calculate the current date plus 5 weekdays (which should exclude weekends), but I also need it to exclude defined holidays. Any help would be greatly appreciated.
// Work Order
// Get template from Google Docs and name it
var docTemplate = ""; // *** replace with your template ID ***
var docName = "Work Order";
// When Form Gets submitted
function onFormSubmit(e) {
//Get information from form and set as variables
var email_address = "";
var job_name = e.values[1];
var ship_to = e.values[11];
var address = e.values[12];
var order_count = e.values[7];
var program = e.values[2];
var workspace = e.values[3];
var offer = e.values[4];
var sort_1 = e.values[5];
var sort_2 = e.values[6];
var print_services = e.values[10];
var priority = e.values[13];
var notes = e.values[14];
var formattedDate = Utilities.formatDate(new Date(), "EDT", "MM/dd/yyyy");
// Get document template, copy it as a new temp doc, and save the Doc's id
var copyId = DriveApp.getFileById(docTemplate)
.makeCopy(docName + ' for ' + job_name)
.getId();
// Open the temporary document
var copyDoc = DocumentApp.openById(copyId);
// Get the document's body section
var copyBody = copyDoc.getActiveSection();
// Replace place holder keys,in our google doc template
copyBody.replaceText('keyJobName', job_name);
copyBody.replaceText('keyShipTo', ship_to);
copyBody.replaceText('keyAddress', address);
copyBody.replaceText('keyOrderCount', order_count);
copyBody.replaceText('keyProgram', program);
copyBody.replaceText('keyWorkspace', workspace);
copyBody.replaceText('keyOffer', offer);
copyBody.replaceText('keySort1', sort_1);
copyBody.replaceText('keySort2', sort_2);
copyBody.replaceText('keyPrintServices', print_services);
copyBody.replaceText('keyPriority', priority);
copyBody.replaceText('keyNotes', notes);
copyBody.replaceText('keyDate', formattedDate);
copyBody.replaceText('keyDue', expirationDate);
// 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 = "New Job Submission";
var body = "Here is the work order for " + job_name + "";
MailApp.sendEmail(email_address, subject, body, {
htmlBody: body,
attachments: pdf
});
// Move file to folder
var file = DriveApp.getFileById(copyId);
DriveApp.getFolderById("").addFile(file);
file.getParents().next().removeFile(file);
}
You can use the below function to get future date which excludes weekends and if any holiday declared in the array.
function addDates() {
var date = new Date(); // yor form date
var hodiday = ["08/09/2017","08/15/2017"]; //Define holiday dates in MM/dd/yyyy
var days = 5; //No of days you want to add
date.setDate(date.getDate());
var counter = 0;
if(days > 0 ){
while (counter < days) {
date.setDate(date.getDate() + 1 );
var check = date.getDay();
var holidayCheck = hodiday.indexOf(Utilities.formatDate(date, "GMT", "MM/dd/yyyy"));
if (check != 0 && check != 6 && holidayCheck == -1) {
counter++;
}
}
}
Logger.log(date) //for this example will give 08/16/2017
return date;
}
Related
I have a sheet that is dynamically updating with leads from my marketing campaign. Currently, it has over 9K rows and will continue to grow.
I need to check values for Col F for leads that came in today; however, when I run a loop, it takes too long since it has 9k+ rows.
I want to run a loop for rows that have today's date only. Is there a way to have my script efficiently look for today's date first (without having to loop through 9k rows), and then start my loop?
Below is what I had originally. The code would start from the top of the sheet and look at Col F values and then email me if a cell had only "az-" instead of a full location value. Content below has been modified for privacy
function Google_Check_Lead_Values() {
var title = "Google | Leads"
var app = SpreadsheetApp;
var Def_url = ("website")
///EDIT ^ URL
var ss = app.openByUrl(Def_url).getSheetByName("Google | Leads");
var last_row = ss.getLastRow();
for (i = 1;i <=last_row; i++) {
var location = ss.getRange(i,6).getValue();
var location_len = location.length;
if (location_len <= 3){
MailApp.sendEmail({
to: "email1",
//cc: "email2",
subject: "Discrepancy found on " + title,
htmlBody:"Hi Team, <br>This is an automated notifcation to let you know that an issue was found on " + title + ". Please take a look at the sheet linked below: <br><br> email"
});//end of email
}//end of if
}//end of for
}//end of function
Try it like this:
function Google_Check_Lead_Values() {
const title = "Google | Leads"
const ss = SpreadsheetApp.openByUrl("url")
const sh = SpreadsheetApp.openByUrl(Def_url).getSheetByName(title);
const dt = new Date();
const dtv = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate()).valueOf();
const vs = sh.getDataRange().getValues();
vs.forEach(r => {
let loc = r[5].toString();
let d = new Date(r[0]);
let dv = new Date(d.getFullYear(),d.getMonth(),d.getDate()).valueOf();
if( dv == dtv && loc.length < 4) {
MailApp.sendEmail({ to: "email1", subject: "Discrepancy found on " + title, htmlBody: "Hi Team, <br>This is an automated notifcation to let you know that an issue was found on " + title + ". Please take a look at the sheet linked below: <br><br> email", })
}
});
}
I believe your goal is as follows.
You want to reduce the process cost of the search of value from a large sheet.
You want to achieve this using Google Apps Script.
In your situation, by searching the values from the columns "A" and "F", you want to send emails.
In order to search the values from a sheet, there are several methods. Using for loop, TextFinder and query language. In my test, when the query language is used, the process cost was the lowest of these 3 patterns. So, in this answer, I would like to propose to search the values using use the query language. Ref
Sample script:
function Google_Check_Lead_Values() {
var title = "Google | Leads"
var Def_url = "###"; // Please use your Spreadsheet URL.
var ss = SpreadsheetApp.openByUrl(Def_url);
const spreadsheetId = ss.getId();
const today = new Date();
const tommorow = new Date();
tommorow.setDate(tommorow.getDate() + 1);
const vToday = Utilities.formatDate(today, Session.getScriptTimeZone(), "yyyy-MM-dd");
const vTommorow = Utilities.formatDate(tommorow, Session.getScriptTimeZone(), "yyyy-MM-dd");
const sheetName = "Google | Leads";
const query = `SELECT * WHERE A >= DATE '${vToday}' AND A < date '${vTommorow}' AND E = 'az-'`;
const url = `https://docs.google.com/spreadsheets/d/${spreadsheetId}/gviz/tq?sheet=${sheetName}&tqx=out:csv&tq=${query}`;
const res = UrlFetchApp.fetch(encodeURI(url), { headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() } });
const [, ...values] = Utilities.parseCsv(res.getContentText());
if (values.length == 0) return;
values.forEach(_ => {
MailApp.sendEmail({
to: "email1",
//cc: "email2",
subject: "Discrepancy found on " + title,
htmlBody: "Hi Team, <br>This is an automated notifcation to let you know that an issue was found on " + title + ". Please take a look at the sheet linked below: <br><br> email"
});
});
}
When this script is run, the today date values are searched from the column "A", and also it searches whether the cell value of column "F" is the value of az-. When the values are found, the emaiil is sent. In this case, from your showing script, for example, when 2 rows are found, 2 emails are sent.
If you know the Spreadsheet ID, you can also directly use it to const spreadsheetId = ss.getId(); like const spreadsheetId = "###SpreadsheetID###";.
In this sample script, about the search of the column "F", it seaches whether the value is az-. But, if you want to search the length of cell value, please modify as follows.
From
const query = `SELECT * WHERE A >= DATE '${vToday}' AND A < date '${vTommorow}' AND E = 'az-'`;
To
const query = `SELECT * WHERE A >= DATE '${vToday}' AND A < date '${vTommorow}' AND E MATCHES '^.{0,3}$'`;
References:
Query Language Reference (Version 0.7)
Related thread.
https://stackoverflow.com/a/56663884
I have been trying to find the right method to pre-fill the start date field in my Google Forms with the current date and time, but I failed to achieve this.
I think the createResponse(hour, minute) would be the best method, however I have absolutely no idea on where to include it in my script:
function book1() {
var form = FormApp.getActiveForm();
var responses = form.getResponses();
var len = responses.length;
var last = len - 1;
var items = responses[last].getItemResponses();
////// var current= items[2].createResponse(10, 00);
var email = responses[last].getRespondentEmail();
var equipment = items[1].getResponse();
var datestart = items[2].getResponse();
var dateend = items[3].getResponse();
var cal = CalendarApp.getCalendarsByName(equipment)[0];
Logger.log('current '+current);
Logger.log(datestart);
Logger.log(dateend);
var start = new Date(datestart);
var end = new Date(dateend);
Logger.log('start '+start);
Logger.log('end '+end);
var allEvents = CalendarApp.getCalendarsByName(equipment)[0].getEvents(start, end);//Returns an array of events
if (allEvents.length < 1) {
var event = cal.createEvent(equipment, start, end)
.addGuest(email);
MailApp.sendEmail({
to: email,
subject: "Equipment " +equipment+ " booking confirmed",
htmlBody: "Equipment " +equipment+ " available, please return it by " +dateend+ " and scan the QR code when returning it.",
});
}
else {
var blob = HtmlService.createHtmlOutputFromFile("calendariframe").getBlob();
MailApp.sendEmail({
to: email,
subject: "Equipment " +equipment+ " not available",
htmlBody: blob.getDataAsString(),
});
};
}
In my latest attempt, I encountered that error;TypeError: items[2].createResponse is not a function
Would you know how to make it work?
Knowing that pre-filling the date and time only simplifies the process of the form users, but that in some cases they will not use the current date and time as a starting time for booking.
Thanks a lot in advance!
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);
}
}
Good Morning Stack!
I was looking for some advice for some expanded functionality on a Google Script that helps me track and process absence requests. The process is as follows:
A user submits their form responses
The form responses are stored on a spreadsheet
A google doc, pre-created, is pulled and the values from the spreadsheet are implanted in the document.
The document is converted to a PDF
That PDF is automatically emailed to me.
However, some work policies have changed and I now need this form to have a signature that is unique to the person filling the form. In the past we simply printed the resulting PDF and had the person sign off on it.
I know I do not have the technicality to add on any sort of Electronic Signature functionality, nor is emailing that PDF to them an option as it doesn't create a fillable PDF (and my users don't really know how to digitally sign items anyways)
So what I was thinking is to create a form unique to each of the 15-20 users of this process and instead of a getFileByID, have the script check the Google Users account / email and pull the file created for them and stored in my drive instead.
As follows is my current, functional, script. How could I make this work?
function onFormSubmit(e) {
var Last_Name = e.values[2];
var First_Name = e.values[3];
var Middle_Initial = e.values[4];
var Work_Location = e.values[5];
var Job_Title = e.values[6];
var Contact_Number = e.values[7];
var Start = e.values[8];
var End = e.values[9];
var Time = e.values[10];
var Reason = e.values[11];
var D = new Date();
var copyId = DriveApp
.getFileById("1sdfjlsdf55asdfnk565enasdfnsnsd2")
.makeCopy("AbsenceRequest" + Last_Name + Start).getId();
var copyDoc = DocumentApp.openById(copyId)
var copyBody = copyDoc.getActiveSection();
copyBody.replaceText('keyLastName', Last_Name);
copyBody.replaceText('keyFirstName', First_Name);
copyBody.replaceText('keyMiddleInitial', Middle_Initial);
copyBody.replaceText('keyWorkLocation', Work_Location);
copyBody.replaceText('keyJobTitle', Job_Title);
copyBody.replaceText('keyContactNumber',Contact_Number);
copyBody.replaceText('keyStart', Start);
copyBody.replaceText('keyEnd', End);
copyBody.replaceText('keyTime', Time);
copyBody.replaceText('keyDate', D);
copyDoc.saveAndClose();
var pdf = DriveApp.getFileById(copyId).getAs("application/pdf");
var Email = "email#email.org" + "," + "email#email.org";
var Subject = "Absence Request"
var Body = "This is an absence request"
MailApp.sendEmail(Email, Subject, Body, {attachments: pdf});
DriveApp.getFileById(copyId).setTrashed(true);
}
I built an app that I then built with PhoneGap Build.THe purpose is for it to run a code (starts at var Quotes once per day when the app is loaded).
When debugging why it wasn't working I noticed that in console I was getting back my message "Local storage didn't work". This means that my initial localstorage.getItem which is supposed to make sure the local storage can be read is returning null. So my code never gets executed.
What am I doing wrong?
function onDeviceReady() { //Do something when the app on device is loaded
var localVal = localStorage.getItem('DateOpened');
if(localVal == null){
console.log("LocalStorage did not work...")}
else
{
var tempd = new Date(); //Get today's date
var str = tempd.getDay() + tempd.getMonth() + tempd.getFullYear();
if(localVal.localeCompare(str) == -1)
{
var Quotes = [];
var ID = [];
var Tag = [];
var seen = [];
localStorage.setItem('DateOpened',str);
console.log("The App Ran, you can get a new fat tomorrow");
console.log("Todays date:" + str);
}
}
}
Initially, there will be no DateOpened item in local storage, so your code will follow the "did not work" branch, because getItem returns null for things that don't exist. That branch never sets anything in DateOpened, so...you'll always follow that branch.
The fix is not to skip over your code setting DateOpened if the device has local storage.
There's also an unrelated problem: Your var str = tempd.getDay() + tempd.getMonth() + tempd.getFullYear() does not produce a string, it produces a number formed by adding those values together, since they're all numbers. Your later localeCompare will fail because it's not a string. You also have the fields in the wrong order for a meaningful textual comparison — you need year first, then month, then day.
Here's a minimal fix, see comments:
function onDeviceReady() {
var tempd = new Date();
// Note that by adding strings in there, we end up with a string instead of adding.
// Note the order: Year first, then month, then day.
// Also, since we display it, we put separators in and add 1 to month (since Jan = 0).
var str = tempd.getFullYear() + "-" + (tempd.getMonth() + 1) + "-" + tempd.getDay();
var localVal = localStorage.getItem('DateOpened');
// If we have no stored value, or it's more than a day old by your definition,
// do your stuff and store the new date
if (localVal == null || localVal.localeCompare(str) < 0) {
var Quotes = [];
var ID = [];
var Tag = [];
var seen = [];
localStorage.setItem('DateOpened', str);
console.log("The App Ran, you can get a new fat tomorrow");
console.log("Todays date:" + str);
}
}
I think this is help full for you.
function onDeviceReady() { //Do something when the app on device is loaded
var localVal = localStorage.getItem('DateOpened');
if (typeof(DateOpened) == "undefined")
console.log("LocalStorage did not work...")}
else
{
var tempd = new Date(); //Get today's date
var str = tempd.getDay() + tempd.getMonth() + tempd.getFullYear();
var allRecords=JSON.parse(localStorage.getItem("DateOpened"));
if(allRecords == -1)
{
var Quotes = [];
var ID = [];
var Tag = [];
var seen = [];
localStorage.setItem('DateOpened',str);
console.log("The App Ran, you can get a new fat tomorrow");
console.log("Todays date:" + str);
}
}
}