In the post Updating Google Calendars from a Google Spreadsheet, I described a recipe for adding events to a Google Calendar from a Google Spreadsheet using Google Apps Script. After a quick chat with the person who was compiling a spreadsheet they wanted to use to populate a set of calendars, I revisited the script to make a few tweaks and hopefully increase its usability.
So here’s a glimpse of the spreadsheet they’re using to list dates for various campaigns and channels where related activity might occur. Firstly, we have some columns relating to the event or activity, and the dates on which they occur. The first column (added to calendar) is a control switch that identifies that the calendar details have been updated for that event:
Within the spreadsheet, I set the two date columns to have the Date type (from the Tools menu, set the Data Validation option to Date). I’m not sure how the spreadsheet is (correctly) identifying the US date format (MM-DD-YY) – maybe from a US timezone as a global setting for the spreadsheet?
As well as various other admin columns, there are columns relating to whether or not a channel will be used to support a particular event:
From what I could ascertain, the way the spreadsheet is supposed to work goes along the lines of: someone adds details of an event and the associated channels for the event to the spreadsheet. “Add” in a channel column says that event is to be added to that channel calendar. When the updating script is run, for each event it checks the control column A to see that an event hasn’t been added to the various channel calendars, and if it hasn’t checks the channel columns; if a channel column is set to “Add” the event details are added to that event calendar.
So – how do I need to modify the original script? Firstly, the original script use the default calendar. In this case, we need a separate calendar for each channel, so in Google Apps I created one calender per channel:
We can grab a calendar by name from a spreadsheet apps script using a call of the form:
var cal_broadcast=CalendarApp.openByName("broadcastDemo");
When the script runs, we need to grab the appropriate range of cells from the spreadsheet to see which calendars to update. For testing purposes, I only grabbed a few rows…
var startRow = 2; // First row of data to process var numRows = 4 // Number of rows to process var dataRange = sheet.getRange(startRow, 1, numRows, 26); var data = dataRange.getValues(); for (i in data) { var row = data[i]; var title = row[1]; var desc=row[15]; var added = row[col_added]; //Check to see if details for this event have been added to the calendar(s) var tstart = row[2]; //start time - I have defined the column in the spreadsheet as a Date type var tstop = row[3]; //start time - I have defined the column in the spreadsheet as a Date type var broadcast=row[col_broadcast]; // is this event one to "Add" to the broadcast calendar? var itunes=row[col_itunes]; // is this event one to "Add" to the itunes calendar? etc var youtube=row[col_youtube]; if (added!="Added") { //the calendar(s) have not been updated for this event if (broadcast=="Add") { cal_broadcast.createEvent(title, tstart,tstop, {description:desc}); //add the event to the "broadcast" calendar } if (itunes=="Add"){ cal_itunes.createEvent(title, tstart,tstop, {description:desc}); } // etc for each channel var v = parseInt(i)+2; // +2 is an offset to do with the numbering of rows and the "blank" header row 0; sheet.getRange(v, 1, 1, 1).setValue("Added"); //set the fact that we have updated the calendars for this event } }
In order to identify which columns to use to identify the broadcast, itunes, etc channels, I went defensive (the following bit of code comes before the previous snippet; what is does is to look at each column heading, and then set the column number for each channel appropriately based on its name; I should probably use a similar technique to identify the start/stop dates. What this approach does is accommodate changes to the spreadsheet in terms of the insertion of additional columns or the reordering of columns, for example, at a later date):
var ss=SpreadsheetApp.getActiveSpreadsheet(); var sheet=SpreadsheetApp.setActiveSheet(ss.getSheets()[0]); //need a routine to set active sheet by name? //go defensive var col_broadcast,col_itunes, col_youtube=1; var maxcols=sheet.getMaxColumns(); for (var j=1;j<=maxcols;j++){ var header= sheet.getRange(1, j, 1, 1).getValue(); switch(header){ case "Added to Google (Y/N/Hold)":col_added=j-1; case "Broadcast":col_broadcast=j-1; break; case "iTunes":col_itunes=j-1; break; case "YouTube": col_youtube=j-1; break; default: } }
Running the combined function thus searches the spreadsheet for the appropriate channel columns and control column, checks the control column for each event entry to ensure that the event hasn’t been added to the selected calendars, and then adds the event to the appropriate channel calendars if required.
Playing with the script, it seemed a little bit clunky, so I tweaked it to update the channel cells with the word “Added” if it had been set to Add, and the calendar had been updated:
if (broadcast=="Add") { cal_broadcast.createEvent(title, tstart,tstop, {description:desc}); dataRange.getCell(parseInt(i)+1,col_broadcast+1).setValue('Added'); // Replace "Add" with "Added"; +1 is offset for sheet numbering }
It also struck me that if the settings of a channel was updated to “Add” after that event was updated, that channel’s calendar would never get updated. So I created a variant of the updating function that would just run on a per column basis and update a calendar entry for an event if it was set to “Add”, rather than checking the control column:
function caltestAddtoCal_broadcast(){ caltestAddtoCal("broadcast"); } function caltestAddtoCal(addCal){ //... if (addCal!="") { if ((addCal=="broadcast")&&(broadcast=="Add")) { cal_broadcast.createEvent(title, tstart,tstop, {description:desc}); dataRange.getCell(parseInt(i)+1,col_broadcast+1).setValue('Added'); //+1 is offset for sheet numbering } // ... } }
What this means is is that a channel controller can update entries in their calendar by running the script just for that channel and adding “Add” to any event they want adding to the calendar, the list of “Added” entries showing which events have already been added to that calendar:
Having doodled a script that sort of works, it’s now time to hack it around it so it looks a little more elegant. Which means refactoring.. sigh… and another reprise in a day or two, I guess…?!