7
\$\begingroup\$

I need help with a simple Google Apps Script I've written. The script finds days on which no all-day events have been scheduled, across multiple calendars.

I'm fairly certain the main issue is the fact that I'm making an API call to get all owned calendars, then I'm making 5 API calls on each of these calendars to retrieve each day's (Mon - Fri) events.

Currently, it takes about 60 seconds to run on an account with 11 owned calendars. Is there anything that can be done to optimize?

var one_day = 86400000; //24 * 60 * 60 * 1000
var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
var startDay = nextMonday(new Date());
// Script-as-app template.
function doGet() {
 var app = UiApp.createApplication();
 var button = app.createButton('Find Blanks').setId('button');
 var loader = app.createImage('http://preloaders.net/preloaders/496/Flip%20Flop.gif').setVisible(false).setId('loader');
 var blanks = app.createHTML().setId('blanksArea').setHTML('Click button to find calendar blanks for week of ' + startDay);
 app.add(button);
 app.add(app.createHTML('<br/>'));
 app.add(loader);
 app.add(blanks);
 var clientHandler = app.createClientHandler().forEventSource().setEnabled(false).forTargets(loader).setVisible(true).forTargets(blanks).setHTML('Processing for week of ' + startDay);
 var handler = app.createServerHandler('serverHandler');
 button.addClickHandler(clientHandler);
 button.addClickHandler(handler);
 return app;
}
function serverHandler(e) {
 var app = UiApp.getActiveApplication();
 var button = app.getElementById('button');
 var blanks = app.getElementById('blanksArea');
 var calendarsWithBlanks = getCalendarsWithBlanks();
 blanks.setHTML(calendarsWithBlanks.length ? calendarsWithBlanks.join('<br/>') : 'There are no blanks');
 app.getElementById('loader').setVisible(false);
 button.setEnabled(true);
 app.close();
 return app;
}
function getCalendarsWithBlanks()
{
 var calendars = CalendarApp.getAllOwnedCalendars();
 var calendarsWithBlanks = [];
 for(var i = 0; i < calendars.length; i++)
 {
 var calendar = calendars[i];
 var calendarName = calendar.getName();
 var blankDays = checkCalendarForBlanks(calendar, startDay);
 if(blankDays.length)
 {
 calendarsWithBlanks.push(calendarName + ' has ' + blankDays.join(', ') + ' blank')
 }
 }
 return calendarsWithBlanks;
}
function checkCalendarForBlanks(calendar, startDay)
{
 var blankDays = [];
 for(var d = 0; d < 5; d++)
 {
 var currDay = new Date(startDay.getTime() + (d * one_day));
 var events = getEventsForDay(calendar, currDay);
 var foundAllDayEvent = false;
 for(var e = 0; e < events.length; e++)
 {
 if(events[e].isAllDayEvent())
 {
 foundAllDayEvent = true;
 break;
 }
 }
 if(!foundAllDayEvent)
 {
 blankDays.push(days[currDay.getDay()]);
 }
 }
 return blankDays;
}
function nextMonday(date)
{
 var today = date;
 today.setUTCHours(0);
 today.setMinutes(0);
 today.setSeconds(0);
 today.setMilliseconds(0);
 var day = today.getDay();
 var offset = 8 - day
 var next_monday = new Date(today.getFullYear(), today.getMonth(), today.getDate()+offset);
 return next_monday;
}
function getEventsForDay(calendar, date)
{
 /* !!!KLUDGE ALERT!!!
 * This is a kludge. The API method getEventsForDay is wonky: http://stackoverflow.com/questions/11870304/google-apps-script-geteventsforday-returns-invalid-recurring-events
 */
 var startOfDay = new Date(date.getTime());
 startOfDay.setUTCHours(0);
 startOfDay.setMinutes(0);
 startOfDay.setSeconds(0);
 startOfDay.setMilliseconds(0); 
 var endOfDay = new Date(startOfDay.getTime() + one_day);
 return calendar.getEvents(startOfDay, endOfDay)
}
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Mar 24, 2014 at 17:25
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

I flipped your logic a bit in the example below. I only do one API call per calendar and then do all of the processing in the client. It seems a bit faster for me, but I'm not seeing the 60 second processing time that you are. So your results my vary.

var one_day = 86400000; //24 * 60 * 60 * 1000
var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
var startDay = nextMonday(new Date());
// Script-as-app template.
function doGet() {
 var app = UiApp.createApplication();
 var button = app.createButton('Find Blanks').setId('button');
 var loader = app.createImage('http://preloaders.net/preloaders/496/Flip%20Flop.gif').setVisible(false).setId('loader');
 var blanks = app.createHTML().setId('blanksArea').setHTML('Click button to find calendar blanks for week of ' + startDay);
 app.add(button);
 app.add(app.createHTML('<br/>'));
 app.add(loader);
 app.add(blanks);
 var clientHandler = app.createClientHandler().forEventSource().setEnabled(false).forTargets(loader).setVisible(true).forTargets(blanks).setHTML('Processing for week of ' + startDay);
 var handler = app.createServerHandler('serverHandler');
 button.addClickHandler(clientHandler);
 button.addClickHandler(handler);
 return app;
}
function serverHandler(e) {
 var app = UiApp.getActiveApplication();
 var button = app.getElementById('button');
 var blanks = app.getElementById('blanksArea');
 var calendarsWithBlanks = getCalendarsWithBlanks();
 blanks.setHTML(calendarsWithBlanks.length ? calendarsWithBlanks.join('<br/>') : 'There are no blanks');
 app.getElementById('loader').setVisible(false);
 button.setEnabled(true);
 app.close();
 return app;
}
function test_getCalendarsWithBlanks() {
 getCalendarsWithBlanks().forEach(function(calendarResult) {
 Logger.log(calendarResult);
 });
 }
function getCalendarsWithBlanks()
{
 var calendars = CalendarApp.getAllOwnedCalendars();
 var calendarsWithBlanks = [];
 for(var i = 0; i < calendars.length; i++)
 {
 var calendar = calendars[i];
 var calendarName = calendar.getName();
 var blankDays = checkCalendarForBlanks(calendar, startDay);
 if(blankDays.length )
 {
 calendarsWithBlanks.push(calendarName + ' has ' + blankDays.join(', ') + ' blank')
 }
 }
 return calendarsWithBlanks;
}
function checkCalendarForBlanks(calendar, startDay)
{
 var blankDays = [],
 allDayEventsPerDay = [0,0,0,0,0,0,0],
 endDate = new Date(startDay.getTime() + (4 * one_day)),
 // get all events for the next work week
 allEventsForNextWorkWeek = calendar.getEvents(startDay, endDate);
 //count number of all day events on each day
 allEventsForNextWorkWeek.forEach(function(event){
 if(event.isAllDayEvent()) {
 //count each day the event is on
 determineDaysOnWhichEventTakesPlace(event,startDay,endDate).forEach(function(eventDate){
 allDayEventsPerDay[eventDate.getDay()]++; 
 });
 }
 });
 //figure out which ones have 0 (ie blank days)
 allDayEventsPerDay.forEach(function(count, offset){
 if(count === 0) {
 blankDays.push(days[offset]);
 }
 });
 return blankDays;
}
function determineDaysOnWhichEventTakesPlace(event, lowerLimit, upperLimit) {
 var startDate = new Date(Math.max(event.getAllDayStartDate().getTime(), lowerLimit.getTime())),
 endDate = new Date(Math.min(event.getAllDayEndDate().getTime(), upperLimit.getTime())),
 dates =[],
 eventDate;
 for(eventDate = startDate; eventDate< endDate; eventDate = new Date(eventDate.getTime() + (one_day))) {
 dates.push(eventDate);
 }
 return dates;
}
function nextMonday(date)
{
 var today = date;
 today.setUTCHours(0);
 today.setMinutes(0);
 today.setSeconds(0);
 today.setMilliseconds(0);
 var day = today.getDay();
 var offset = 8 - day
 var next_monday = new Date(today.getFullYear(), today.getMonth(), today.getDate()+offset);
 return next_monday;
}
answered Jun 1, 2014 at 13:23
\$\endgroup\$
2
  • \$\begingroup\$ This is definitely faster, but something has gone hinky and it's now reporting false positives (blanks are reported where they don't exist). In any case, you've solved the performance issue. Thanks! \$\endgroup\$ Commented Jun 2, 2014 at 16:05
  • 2
    \$\begingroup\$ There could be a bit of wonkiness in checkCalendarForBlanks() with respect to how start and end dates are defined by all day events. Perhaps adding the total number of seconds per day isn't the way to go. Maybe you need to explicitly build date objects for each day and then do a comparison. \$\endgroup\$ Commented Jun 2, 2014 at 16:31

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.