Setting cell value with two dates - google-apps-script

I'm creating a Google Spreadsheet script with Google App scripts that interacts with Google Calendar. What I'm doing is searching for a specific Calendar event by keyword, pulling the start and end dates from that event, and then the goal is to display those two dates into one spreadsheet cell (mostly for readability sake). In other words, the cell should look like '07/14/19 - 07/20/19.' My code below:
function listEvents() {
var id = "THE ID FOR MY CALENDAR IS HERE";
var cal = CalendarApp.getCalendarById(id);
var startPeriod = new Date();
startPeriod.setHours(0, 0, 0, 0);
var endPeriod = new Date(startPeriod);
endPeriod.setDate(endPeriod.getDate() + 1000);
var sheet = SpreadsheetApp.getActiveSheet();
var gig = sheet.getRange(1,1);
var gigName = gig.getValue(); // get the gig name so you can search for it
var events = cal.getEvents(startPeriod, endPeriod, {search:gigName});
// find the start date of each event in the calendar
var starttime = [];
for (var i = 0; i < events.length; i++) {
starttime.push([events[i].getStartTime()]);
}
// find the end date of each event in the calendar
var endtime = [];
for (var i = 0; i < events.length; i++) {
endtime.push([events[i].getEndTime()]);
var cell = sheet.getRange("E5");
cell.setValue(starttime+endtime);
This is sort of working. I can set starttime in one cell, and endtime in another cell, and they look all nice and pretty ('07/14/19','07/14/19') but when I do cell.setValue(starttime+endtime); the formatting of that cell changes to this messy stuff Sun Jul 14 2019 00:00:00 GMT-0500 (CDT)Sat Jul 20 2019 00:00:00 GMT-0500 (CDT) Just to reiterate, I'd like it to look like '07/14/19-07/20/19.'
I can't tell if this is a code problem or a formatting problem. If anyone can offer some help, it'd be appreciated!

Requirement:
Formatted dates to be concatenated inside .setValue().
Solution:
Format the dates before you concatenate them in your .setValue(). Use the following 3 lines of code to replace the ones currently in your script.
//format the start time
starttime.push([Utilities.formatDate(events[i].getStartTime(), "GMT", "dd/MM/yy")]);
//format the end time
endtime.push([Utilities.formatDate(events[i].getEndTime(), "GMT", "dd/MM/yy")]);
Then your .setValue() should look something like this:
cell.setValue(starttime + ' - ' + endtime);
Example:
function listEvents() {
var id = "THE ID FOR MY CALENDAR IS HERE";
var cal = CalendarApp.getCalendarById(id);
var startPeriod = new Date();
startPeriod.setHours(0, 0, 0, 0);
var endPeriod = new Date(startPeriod);
endPeriod.setDate(endPeriod.getDate() + 1000);
var sheet = SpreadsheetApp.getActiveSheet();
var gig = sheet.getRange(1,1);
var gigName = gig.getValue(); // get the gig name so you can search for it
var events = cal.getEvents(startPeriod, endPeriod, {search:gigName});
// find the start date of each event in the calendar
var starttime = [];
for (var i = 0; i < events.length; i++) {
starttime.push([Utilities.formatDate(events[i].getStartTime(), "GMT", "dd/MM/yy")]);
}
// find the end date of each event in the calendar
var endtime = [];
for (var i = 0; i < events.length; i++) {
endtime.push([Utilities.formatDate(events[i].getEndTime(), "GMT", "dd/MM/yy")]);
}
var cell = sheet.getRange("E5");
cell.setValue(starttime + ' - ' + endtime);
Reference:
Utilities.formatDate

You need to look at the Utilities Service... There is a "Format Date" method there that will allow you to format the date to whatever you want it to look like.

Related

Add events from Google sheets to google calendar

I am trying to write scirpt to add events from a spreadsheet to my google calendar. This is the script that I am using.
function addEvents() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lr = ss.getLastRow();
var cal = CalendarApp.getCalendarById("c_fe882662e583725f15fd4faa8c8fdf5124f3affe543778ecdcf0838d6eb17f26#group.calendar.google.com")
var data = ss.getRange("A3:D"+lr).getValues();
for(var i = 0;i<data.length;i++){
cal.createEvent(data[i][2],data[i][4],data[i][5],{location: data[i][6], description: data[i][7]});
}
}
When I run the script I am getting the following error. Error Exception: Invalid argument: startTime addEvents # Code.gs:10
This is the sheet that I am using with my dates.
https://docs.google.com/spreadsheets/d/1qG68-NLnq9LscPPzlnzRLCfHFIsN3v7V5zvWiVsG0qU/edit?usp=sharing
I want the title of the event to be column C, the start time to be Column E, the endtime to be Column F, Location G, and Description H.
Modification points:
In your script, in the for loop, data[i][4],data[i][5] is used as the start and end time. And also, data[i][7] is used. But, atvar data = ss.getRange("A3:D"+lr).getValues();, 4 columns of "A" to "D" are retrieved. I thought that this might be the reason for your issue. In this case, it is required to be var data = ss.getRange("A3:H" + lr).getValues().
But, when I saw your Spreadsheet, the start and end times don't have the year and month. In this case, 1899 year is used. Please be careful about this. From your Spreadsheet, I guessed that you might have wanted to use the year, month, and date from column "A".
When my understanding of your current issue and your goal, how about the following modification?
Modified script:
function addEvents() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lr = ss.getLastRow();
var cal = CalendarApp.getCalendarById("c_fe882662e583725f15fd4faa8c8fdf5124f3affe543778ecdcf0838d6eb17f26#group.calendar.google.com");
var data = ss.getRange("A3:I" + lr).getValues();
while (data[data.length - 1][0] == '') data.pop();
for (var i = 0; i < data.length; i++) {
var year = data[i][8].getFullYear();
var month = data[i][8].getMonth();
var date = data[i][8].getDate();
data[i][4].setFullYear(year);
data[i][4].setMonth(month);
data[i][4].setDate(date);
data[i][5].setFullYear(year);
data[i][5].setMonth(month);
data[i][5].setDate(date);
cal.createEvent(data[i][2], data[i][4], data[i][5], { location: data[i][6], description: data[i][7] });
}
}
When this script is run, the start and end times are retrieved from the columns "E" and "F", respectively. And also, the year, month, and date are retrieved from column "A". Using these values, the start and end date are created and they are used with createEvent.
When you want to use other values of year, month, and date instead of column "A", please tell me.
Note:
From your reply of This sounds promising, the sheet that I am using is actually setting up a mail merge as well and the date in column A is for the mail merge and not for the calendar. I would actually like column I to be the date for the calendar events. , I modified the above script.
From your reply of If I run this script twice (or multiple times) as I will continue to add events, it seems to duplicate the events that are already added. Any idea how to eliminate that?, I updated the above script as follows.
function addEvents() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lr = ss.getLastRow();
var cal = CalendarApp.getCalendarById("c_fe882662e583725f15fd4faa8c8fdf5124f3affe543778ecdcf0838d6eb17f26#group.calendar.google.com");
var data = ss.getRange("A3:R" + lr).getValues();
while (data[data.length - 1][0] == '') data.pop();
var rangeList = [];
for (var i = 0; i < data.length; i++) {
if (data[i][17] == "created") continue;
var year = data[i][8].getFullYear();
var month = data[i][8].getMonth();
var date = data[i][8].getDate();
data[i][4].setFullYear(year);
data[i][4].setMonth(month);
data[i][4].setDate(date);
data[i][5].setFullYear(year);
data[i][5].setMonth(month);
data[i][5].setDate(date);
cal.createEvent(data[i][2], data[i][4], data[i][5], { location: data[i][6], description: data[i][7] });
rangeList.push(`R${i + 3}`);
}
if (rangeList.length == 0) return;
ss.getRangeList(rangeList).setValue("created");
}
Try changing this: cal.createEvent(data[i][2],data[i][4],data[i][5],{location: data[i][6], description: data[i][7]}); to this cal.createEvent(data[i][2],new Date(data[i][4]),new Date(data[i][5]),{location: data[i][6], description: data[i][7]});
Try changing this var data = ss.getRange("A3:D"+lr).getValues(); to this var data = ss.getRange("A3:H"+lr).getValues();
Try this:
function addEvents() {
const ss = SpreadsheetApp.getActive();
var sh = ss.getActiveSheet();
var cal = CalendarApp.getCalendarById("jimesteban#jimesteban.com")
var data = sh.getRange("A3:H" + sh.getLastRow()).getValues();
for (var i = 0; i < data.length; i++) {
cal.createEvent(data[i][2], data[i][4], data[i][5], { location: data[i][6], description: data[i][7] });
}
}

Filter gmails using timestamp

Am new dabbling with Google Apps Script; would like to ask if I'm in the right direction, and how to I manipulate time within the script.
I'm struggling in trying to maniuplate time values in Google App Script, basically I am able to pull the timestamp of each email sent, but I only want to paste into the spreadsheet email information that were recent, e.g. within 30minutes from script run time. This is to avoid pulling duplicate information.
Not sure if there is a currentTime() function here, or I have to create a new Date() object and do some calculations from there. Tried a few variations and nothing seemed to work proper.
Would appreciate any help in getting towards the right direction in doing this thank you!
function getDetails(){
var DEST_URL = "SHEET_URL"; //redacted for sensitivity
var DEST_SHEETNAME = "Test";
var destss = SpreadsheetApp.openByUrl(DEST_URL);
var destSheet = destss.getSheetByName(DEST_SHEETNAME);
var threads = GmailApp.search("FILTERS"); //filter settings redacted for sensitivity
for(var i = 0; i < threads.length; i++){
var messages=threads[i].getMessages();
for(var j =0; j < 1; j++){ //only take first message in thread
var message = messages[j];
var subject = message.getSubject() ;
var sentTimeStamp = message.getDate();
if(sentTimeStamp is within last 30minutes as of script run time){ //this is where i need help
var delimitString = subject.split("is sent");
var detailName = delimitString[0];
var lastRow = destSheet.getLastRow();
destSheet.getRange(lastRow + 1,1).setValue(detailName);
destSheet.getRange(lastRow + 1,2),setValue(sentTimeStamp);
}
}
}
}
You can convert timeStamp into ms seconds and then compare to the value of "30 s ago"
Sample:
var sentTimeStamp = message.getDate();
var now = new Date();
var ThirtyMinutesAgo = now-30*60*1000;
if(sentTimeStamp.getTime() < ThirtyMinutesAgo){
...
}
References:
newDate()
getTime()
Another idea would be to query for emails that you received the last 30 minutes.
Explanation:
You can get the emails that you received the last 30 minutes ago as a query in the GmailApp.search function. See this link to see what filters you can use.
This will get the last emails with keyword "FILTERS" that you received the last 30 minutes.
var ThirtyMinutesAgo = new Date();
ThirtyMinutesAgo.setMinutes(ThirtyMinutesAgo.getMinutes() - 30);
const queryString = `"FILTERS" newer:${Math.round(ThirtyMinutesAgo.getTime()/1000)}`
const threads = GmailApp.search(queryString); // threads the last 30 minutes
This approach is more efficient for two reasons:
You have less data (threads) to iterate over with the for loop.
You don't need to apply and if statement on every thread.
Solution:
function getDetails(){
var DEST_URL = "SHEET_URL"; //redacted for sensitivity
var DEST_SHEETNAME = "Test";
var destss = SpreadsheetApp.openByUrl(DEST_URL);
var destSheet = destss.getSheetByName(DEST_SHEETNAME);
// var threads = GmailApp.search("FILTERS"); //filter settings redacted for sensitivity
// new code
var ThirtyMinutesAgo = new Date();
ThirtyMinutesAgo.setMinutes(ThirtyMinutesAgo.getMinutes() - 30);
const queryString = `"FILTERS" newer:${Math.round(ThirtyMinutesAgo.getTime()/1000)}`
const threads = GmailApp.search(queryString); // threads the last 30 minutes
//
for(var i = 0; i < threads.length; i++){
var messages=threads[i].getMessages();
for(var j =0; j < 1; j++){ //only take first message in thread
var message = messages[j];
var subject = message.getSubject() ;
var sentTimeStamp = message.getDate();
var delimitString = subject.split("is sent");
var detailName = delimitString[0];
var lastRow = destSheet.getLastRow();
destSheet.getRange(lastRow + 1,1).setValue(detailName);
destSheet.getRange(lastRow + 1,2),setValue(sentTimeStamp);
}
}
}
}

Copy Sheet with protections and change new Duplicate's name to a date

I have a template sheet for group data entry. Most of the sheet is free entry, but there are title rows that I don't want edited so I have them protected. We have one of these tabs for each day of the month and a new Sheet for each month.
I want to copy the template 30-31 times depending on the month and have the title of the sheet be the corresponding date (MM.dd.yy ie: 11.02.20). I have the Date set in A2 (ie: 11/01/2020).
So far I tried combining a protections and a date change, but I keep getting variable errors and then sometimes it double creates sheets (like 11.06.20 and then stops).
This is the code I've tried (and edited and moved around a few times).
function duplicateSheetWithProtections() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var dateCell = "A2";
sheet = ss.getSheetByName('Template.01.20');
sheet2 = sheet.copyTo(ss).setName('11..20');
var N = 30;
var startDate = new Date(s.getRange(dateCell).getValue());
var day = startDate.getDate();
var month = startDate.getMonth();
var year = startDate.getFullYear();
for (var i = 0; i < N; i++) {
var asn = s.copyTo(ss);
var thisSheetDate = new Date(year, month, day+(i+1));
asn.getRange(dateCell).setValue(thisSheetDate);
asn.setName(Utilities.formatDate(thisSheetDate, "GMT-08:00", "MM.dd.yy"));
var protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var p = protections[i];
var rangeNotation = p.getRange().getA1Notation();
var p2 = sheet2.getRange(rangeNotation).protect();
p2.setDescription(p.getDescription());
p2.setWarningOnly(p.isWarningOnly());
if (!p.isWarningOnly()) {
p2.removeEditors(p2.getEditors());
p2.addEditors(p.getEditors());
// p2.setDomainEdit(p.canDomainEdit());
}
}
}
}
Any help would be greatly appreciated.
Also, new to this and if you couldn't tell, kind of a noob. So any references to help grow would be awesome. Thanks!
Issue:
You are using the same variable (i) for two different for loops, one nested inside the other. This is messing up with your dates, causing the error you're getting.
Solution:
Change the variable name of the inner loop (for example, to j):
for (var j = 0; j < protections.length; j++) {
var p = protections[j];
Further issues:
You are setting protections to sheet2, which corresponds to the copied sheet with name 11..20, but not to the rest of sheets (actually, I'm not sure what's the point of making this copy, so I'd just delete the line sheet2 = sheet.copyTo(ss).setName('11..20');). In order to set the protections to each copied sheet, you should use asn instead:
var p2 = asn.getRange(rangeNotation).protect();
Since you want to copy the file named Template.01.20, there is no point in getting the active sheet and storing it in s. I'd just change the mentions of s to sheet (and remove the line var s = ss.getActiveSheet();, since it's not needed):
var startDate = new Date(sheet.getRange(dateCell).getValue());
// ...
var asn = sheet.copyTo(ss);
Since the number of sheets to copy depends on how many days the month has, I'd suggest you to dynamically find that number. You can do that using the following function, for example (credits to Juan Mendes):
function getDaysInMonth(year, month, day) {
var date = new Date(year, month, day);
var days = [];
while (date.getMonth() === month) {
days.push(new Date(date));
date.setDate(date.getDate() + 1);
}
return days;
}
Which you could then call in your main function:
var dates = getDaysInMonth(year, month, day + 1);
for (var i = 0; i < dates.length; i++) {
var asn = sheet.copyTo(ss);
var thisSheetDate = dates[i];
Code sample:
Therefore, your code could be something like this instead:
function duplicateSheetWithProtections() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dateCell = "A2";
var sheet = ss.getSheetByName('Template.01.20');
var startDate = new Date(sheet.getRange(dateCell).getValue());
var day = startDate.getDate();
var month = startDate.getMonth();
var year = startDate.getFullYear();
var dates = getDaysInMonth(year, month, day + 1);
for (var i = 0; i < dates.length; i++) {
var asn = sheet.copyTo(ss);
var thisSheetDate = dates[i];
asn.getRange(dateCell).setValue(thisSheetDate);
asn.setName(Utilities.formatDate(thisSheetDate, "GMT-08:00", "MM.dd.yy"));
var protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var j = 0; j < protections.length; j++) {
var p = protections[j];
var rangeNotation = p.getRange().getA1Notation();
var p2 = asn.getRange(rangeNotation).protect();
p2.setDescription(p.getDescription());
p2.setWarningOnly(p.isWarningOnly());
if (!p.isWarningOnly()) {
p2.removeEditors(p2.getEditors());
p2.addEditors(p.getEditors());
}
}
}
}
function getDaysInMonth(year, month, day) {
var date = new Date(year, month, day);
var days = [];
while (date.getMonth() === month) {
days.push(new Date(date));
date.setDate(date.getDate() + 1);
}
return days;
}

Logging Google mail in Google sheet, Google Apps Script

HELP! I’m using a script I basically cribbed from Tom Woodward at Bionice Teaching to record email messages in a spreadsheet.
http://bionicteaching.com/auto-logging-email-via-google-script/
I need to add a column that collects any labels that have been attached to the messages. I need to get this done for my work, but I'm brand new to Google Apps Script and really need someone to hold my hand... Essentially doing it for me, then teaching me what it was you did. I really appreciate any help you can give me in any case. Thanks
Here is what I’m using:
function myFunction() {
//this is just the stuff that recognizes what spreadsheet you're in
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var sheet = ss.getSheetByName("data"); //gets the right sheet
//this chunk gets the date info
var today = new Date();
var dd = today.getDate()-1;
var mm = today.getMonth()+1; //January is 0 DO NOT FORGET THIS
var yyyy = today.getFullYear();
var yesterday = yyyy + '/' + mm + '/' + dd;
//****************************************************
//searches your GMail for emails written after yesterday
var query = "after:" + yesterday;
var threads = GmailApp.search(query);
Logger.log('threads len ' + threads.length);
Logger.log(query);
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
Logger.log(messages);
for (var m = 0; m < messages.length; m++) {
var supportStats = [];
//here's where you decide what parts of the email you want
var from = messages[m].getFrom(); //from field
Logger.log(from);
var time = messages[m].getDate();//date field
Logger.log(time);
var subject = messages[m].getSubject();//subject field
Logger.log(subject);
var body = messages[m].getPlainBody();//body field
Logger.log(body);
var mId = messages[m].getId();//id field to create the link later
var mYear = time.getFullYear();
var mMonth = time.getMonth()+1;
var mDay = time.getDate();
var messageDate = mYear + '/' + mMonth + '/' + mDay;
Logger.log('msg date ' + messageDate);
//decides what found emails match yesterday's date and push them to an array to write to the spreadsheet
if (messageDate === yesterday) {
supportStats.push(from);
supportStats.push(time);
supportStats.push(subject);
supportStats.push(body);
supportStats.push('https://mail.google.com/mail/u/0/#inbox/'+mId); //build the URL to the email
SpreadsheetApp.getActiveSheet().appendRow(supportStats); //writes to the spreadsheet
}
}
}
}
Here is the results I'm getting... Perfect!
Except I'd like one more column that adds the labels that are on each message. How do I do that?
spreatsheet results of Google Apps script mail->sheet
You can use this:
var labels = threads[i].getLabels();
GmailThread::getLabels()
GmailThread has labels, not GmailMessage. It returns an array of labels. Maybe use:
var labelsString = "";
var labelArray = []
for each (var label in labels)
{
labelArray.push(label.getName());
}
if (labelArray.length > 0)
{
labelsString = labelArray.join(',');
}
to insert into the row of the spreadsheet.
GmailLabel::getName()

Want to Add Custom Google Calendar Notifications to Events Created from Google Sheets

I'm currently working on creating a Google Sheet that would allow me to create calendar events (reminders) for when certain reports are due for me.
Basically, on the basis of a given opening date, I have 5 different types of reports due, some at different intervals (ie, 45 days from the open date, 6 months, 12 months). I've been able to modify a sample I found online (http://www.adammcfarland.com/2013/08/09/tutorial-using-google-spreadsheets-to-update-a-google-calendar-our-new-vacation-management-system/), customizing a Google Sheet to generate due dates for each type of report, and create calendar entries based on those due dates. It's actually really cool and powerful.
Unfortunately, I'm stumbling when it comes to creating notifications for the different reports. I know how to create default notifications within the Google Calendar interface, but the quirk I'm currently trying to address is that of these various reports, not all require as much time to complete, so I'm looking to create notifications specific to each report type, and I'm thus far been wholly unable to get things working.
Here's a copy of the script I'm using.
enter code herefunction pushToCalendar() {
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var range = sheet.getRange(2,1,lastRow,5);
var values = range.getValues();
var updateRange = sheet.getRange('G1');
var calendar = CalendarApp.getCalendarById('jk.com_5e9gk4#group.calendar.google.com')
updateRange.setFontColor('red');
var numValues = 0;
for (var i = 0; i < values.length; i++) {
if ((values[i][0].length > 0) && (values[i][2].length > 0)) {
if (values[i][3] != 'y') {
var newEventTitle = 'Note Due: ' + values[i][0] + ' - ' + values[i][2];
var newEvent = calendar.createAllDayEvent(newEventTitle, values[i][1]);
var newEventId = newEvent.getId();
sheet.getRange(i+2,4).setValue('y');
sheet.getRange(i+2,5).setValue(newEventId);
}
Where "values[i][2]" corresponds to the type of report due.
Thanks in advance.
You can try to addreminder just after creating event based on a table. See the code below :
function pushToCalendar() {
var sheet = SpreadsheetApp.getActiveSheet();
//Define reminder, value in minute and lower 4weeks (=40320 minutes)
var reminder = {"type1":1440,//=1 day
"type2":7200,//5days
"type3":20160//2weeks
};
//End define reminder
var lastRow = sheet.getLastRow();
var range = sheet.getRange(2,1,lastRow,5);
var values = range.getValues();
var updateRange = sheet.getRange('G1');
var calendar = CalendarApp.getCalendarById('jk.com_5e9gk4#group.calendar.google.com')
updateRange.setFontColor('red');
var numValues = 0;
for (var i = 0; i < values.length; i++) {
if ((values[i][0].length > 0) && (values[i][2].length > 0)) {
if (values[i][3] != 'y') {
var newEventTitle = 'Note Due: ' + values[i][0] + ' - ' + values[i][2];
var newEvent = calendar.createAllDayEvent(newEventTitle, values[i][1]);
//Add reminder
//For that we assume you well created the reminder var and all type have the amount of minutes define if not you must implement the check in he code
newEvent.addEmailReminder(reminder[values[i][2]]);
//End add reminder
var newEventId = newEvent.getId();
sheet.getRange(i+2,4).setValue('y');
sheet.getRange(i+2,5).setValue(newEventId);
}
}
}
}
I added some comments in the code for you.
Stéphane