Trying to add Guests in Spreadsheet Script to Calendar App - google-apps-script

I am trying to add Guests in "Options" for automatically add a schedule from Google Sheets into Calendar. I have watched videos (which don't discuss this and lead to no answers when others ask this question) and don't know enough to find the CalendarApp info helpful.
Can someone help? (FYI, I also want to stop duplicating events every time this is run) This is my Script:
function addEvents(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lr = ss.getLastRow();
var cal = CalendarApp.getCalendarById("c_kdaqhj8lkd7u68s8thinbnjpik#group.calendar.google.com");
var events = cal.getEvents(new Date ("02/8/2019 12:00 AM"), new Date("02/28/2019 11:59 PM"));
for (var i=0;i<events.length;i++){
var ev = events[i];
ev.deleteEvent();
}
var data = ss.getRange("A2:F"+ lr).getValues();
for(var i = 0;i<data.length;i++){
cal.createEvent(data[i][0], data[i][1], data[i][2], guests:"data[i][3]", "data[i][4]", {description:data[i][5]});
}
}

try this:
cal.createEvent(data[i][0], data[i][1], data[i][2], {guests:`${data[i][3]},${data[i][4]}`, description:data[i][5]});
Reference

Your main problem was that you weren't including guests in the same JavaScript object as description and that guests must be an string including comma separated values. The following script has self explanatory comments and also checks whether an event already exists on the date and time your are trying to insert your event to avoid duplicate events:
function addEvents(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lr = ss.getLastRow();
var cal = CalendarApp.getCalendarById("YOURCALENDARID");
var data = ss.getRange("A2:F"+ lr).getValues();
for(var i = 0;i<data.length;i++){
// According to the documentation if no event was found on those date times it will return null
// However if you delete an event this will return an empty array so we also have to check against that
if(cal.getEvents(data[i][1], data[i][2])==null || cal.getEvents(data[i][1], data[i][2]).length==0 ){
// Options must be a Javascript object and specifically the paramter guests is a string so your data must be integrated
// in such a string
cal.createEvent(data[i][0], data[i][1], data[i][2],{guests: ''+data[i][3]+','+data[i][4]+'', description:data[i][5]});
}
}
}
Reference
createEvent(title,title, startTime, endTime, options), please not the information regarding options and more specifically guests within options.
getEvent(startTime, endTime)

Related

Sheets to GCal Apps Script Trouble Shooting

I have the below code to take a list of events in a Google Sheet and schedule them into a Google Calendar.
function addEvents(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var calId = ss.getRange("C4").getValue();
var cal = CalendarApp.getCalendarById(calId);
var lr = ss.getLastRow();
var data = ss.getRange("B9:F" + lr).getValues();
for(var i = 0;i<data.length;i++){
cal.createEvent(data[i][0], data[i][1], data[i][2], {location: data[i][3], description: data[i][4]});
}
}
I've used this script before with no issues, but now I'm getting the following error message and can't for the life of me figure out what is going on:
TypeError: Cannot read property 'createEvent' of null
I ultimately would like to add an if statement to this code that grabs the newly created Event ID and puts it in the last column of the data on the sheet, and I would like to be asking for help with that, but at this point I just need step one to work.
Any thoughts?

How to update a single event without duplicating the rest of the events

I am currently using app script to sync my google sheet to google calendar. The process is quite simple, my script only takes a date and title from the spreadsheet and creates an all day event on that date with that title.
The problem I am facing is that if I accidentally key in the wrong date or I have to update one of the dates inside the spreadsheet, on running the scheduleShifts function again, all the events are created again which results in many duplicate events that I did not intend to be there. I'm trying to find a solution that helps either updates the title of the event or deletes the event and create a new one in the case where the date that is in the spreadsheet is wrong.
It also isn't very efficient to update the data in the spreadsheet and then update the calendar because in the event where quite a few dates or titles have to be changed, it would take quite a bit of time to change them all in the calendar. It would also be very troublesome to delete the current calendar, create a new one, copy that id into the spreadsheet and then update everything again.
This is what my current code looks like:
function scheduleShifts()
{
/*Identify calendar*/
var spreadsheet = SpreadsheetApp.getActiveSheet();
var calendarId = spreadsheet.getRange("C1").getValue();
var eventCal = CalendarApp.getCalendarById(calendarId);
/*Import data from the spreadsheet*/
var signups = spreadsheet.getRange("C4:F73").getValues();
/*Create events*/
for (x=0; x<signups.length; x++)
{
var shift = signups[x];
var title = shift[0];
var date = shift[3];
eventCal.createAllDayEvent(title, date);
}
}
/*Make the script shareable for others to use*/
function onOpen()
{
var ui = SpreadsheetApp.getUi();
ui.createMenu('Sync to Calendar')
.addItem('Schedule shifts', 'scheduleShifts')
.addToUi();
}
I have tried to avoid duplicating events by retrieving all the events with Advanced Calendar Service, pushing their titles into an array and then verifying with IndexOf. However, I am unsure if this method will work if the title stays the same while there is an update in the date of that event.
The code that I referenced from to do this:
var existingEvents=Calendar.Events.list(calendarId);
var eventArray=[];
existingEvents.items.forEach(function(e){eventArray.push(e.summary)});
for (x=0; x<signups.length; x++) {
var shift = signups [x];
var startTime = shift[0];
var endTime = shift[1];
var inspector = shift[2];
if(eventArray.indexOf(inspector)==-1){
eventCal.createEvent(inspector, startTime, endTime);
}else{
Logger.log('event exists already');
}
}
If anyone needs more info feel free to ask in the comments, your help would be greatly appreciated.
You have developed a script to create events using data from a sheet.
You want to be able to update previously created events while avoiding creating duplicates.
Step 1. Avoid creating duplicates:
In order to avoid creating duplicates, you could make the script write the corresponding eventId when each event has been created. This way, next time the script runs, it can check whether the eventId is populated (in which case the event already exists), and only create the event if it doesn't exist. It could be something like this (in this sample, the eventIds are written to column G):
function scheduleShifts() {
const spreadsheet = SpreadsheetApp.getActiveSheet();
const calendarId = spreadsheet.getRange("C1").getValue();
const eventCal = CalendarApp.getCalendarById(calendarId);
const signups = spreadsheet.getRange("C4:G7").getValues();
for (let x = 0; x < signups.length; x++) {
const shift = signups[x];
const title = shift[0];
const date = shift[3];
const eventId = shift[4];
if (eventId == "") { // Check the event doesn't exist
const newEvent = eventCal.createAllDayEvent(title, date);
const newEventId = newEvent.getId();
spreadsheet.getRange(4 + x, 7).setValue(newEventId); // Write new eventId to col G
}
}
}
Step 2. Update events:
Regarding the update process, I'd suggest you to install an onEdit trigger, either manually or programmatically. This this action requires authorization, a simple onEdit trigger would not work here (see Restrictions).
This way, you can use the event object to only update the event corresponding to the row that was edited, thus avoiding having to update all events every time, which would make the process very inefficient. The update process itself would consist on calling setTitle and setAllDayDate.
The function fired by the onEdit trigger could be something like this:
function updateEvent(e) {
var editedRow = e.range.getRow();
var editedData = e.source.getActiveSheet().getRange(editedRow, 3, 1, 5).getValues()[0];
var eventId = editedData[4];
try {
var event = CalendarApp.getEventById(eventId);
event.setTitle(editedData[0]);
event.setAllDayDate(editedData[3]);
} catch(err) {
console.log("There was a problem updating the event. This event might not exist yet.");
}
}
Notes:
You may not want to manually set the range (C4:G73) if the number of events might vary. You can use methods like getLastRow() no make this range dynamic, based on the spreadsheet content.
The eventId that are used here, corresponding to Class CalendarEvent, are not identical to the API Event resource. Take this into account in case you use the Advanced Service.

Problems with duplicated events when connecting Google Calendar and Google Sheet using AppScript

I successfully integrated GCalendar to Gsheet for creating events. After a Google Form submission, the App Script sends an invitation to my calendar and my guest calendar.
I'm having problems with the automation and the duplicated entries. The script doesn't run when a new row appears - tried both on edit and on form submission -, and when I force it to run, it reschedules ALL the past events.
Here my code
function CreateEvent() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('functionSheet');
var eventCal = CalendarApp.getCalendarById('name#email.com');
var lr = spreadsheet.getLastRow();
var count = spreadsheet.getRange("A2:N"+lr+"").getValues();
for (x=0; x<count.length; x++) {
var shift = count[x];
var summary = shift[2];
var startTime = shift[7];
var endTime = shift[8];
var guests = shift[1];
var description = shift[3];
var location = shift[5];
var event = {
'location': location,
'description': description,
'guests':guests +',',
'sendInvites': 'True',
}
eventCal.createEvent(summary, startTime, endTime, event)
}
}
I'm looking for a solution to improve my script so that it runs every time a new submission has been done AND do not send invitation based on old entries (previous rows).
You are creating calendar events based on Form responses from a Google Form. When your code runs, it is creating events for all the responses and not limited to the most recent response.
The actions to resolve this are two-fold:
trigger the function by using the installable trigger `onFormSubmit.
use Event Objects to capture the form response values, and update the calendar based on those values.
Note: your function is now called CreateEvent(e). The e attribute will automatically give you access to the Event Objects.
The following code is untested, but it indicates the approach to be taken.
`
function CreateEvent(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Logger.log(JSON.stringify(e)); // DEBUG
// identify Calendar
var eventCal = CalendarApp.getCalendarById('<insert id >');
// get the response data
var summary = e.values[2];
var startTime = e.values[7];
var endTime = e.values[8];
var guests = e.values[1];
var description = e.values[3];
var location = e.values[5];
var event = {
'location': location,
'description': description,
'guests':guests +',',
'sendInvites': 'True',
}
// create the event.
eventCal.createEvent(summary, startTime, endTime, event)
}
onFormSubmit
It is important that you trigger your function with the installable trigger onFormSubmit. Refer to the documentation for Managing triggers manually for a set-by-step "how-to" explanation.
Your screen should look something like this when you have completed the setup.
Note that the function name (CreateEvent) does NOT indicate that you are using Event Objects - this is OK - the trigger is just picking up the basic name. BUT it is extremely important that your function is actually called CreateEvent(e) so that it can access the Event Objects.

Create Recurring All Day Calendar Event from List of Dates in Google Sheet

I have a list of dates in column A (starting at A2) paired with text for a title in column B (starting at B2). I have my calendar ID listed in cell E2.
I would like to send this data to Google Calendar to create recurring, all-day calendar events. This calendar should update when the spreadsheet is changed.
Here is what I found for you. it's based on my understanding, may be I'm wrong. Just want to help you.
https://www.quora.com/How-do-I-automatically-add-events-to-a-Google-Calendar-from-a-Google-Sheet
or you can use app to do this task for you here is step by step guide
https://zapier.com/apps/google-calendar/tutorials/how-to-create-calendar-events-from-spreadsheet-tutorial
I wrote this small piece of code that creates recurring events using the data in sheets.
I didn't write this in a trigger, so you would have to run this manually. It could be written in an onEdit trigger, but I don't think it would be the best idea, since you would soon end up having mountains of duplicate events, even though this could be avoided by adding some condition that checks whether an event with those characteristics already exists:
function createEvents() {
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var firstRow = 2;
var firstCol = 1;
var numRows = lastRow - firstRow + 1;
var numCols = 2;
var data = sheet.getRange(firstRow, firstCol, numRows, numCols).getValues();
var calId = sheet.getRange("E2").getValue();
var cal = CalendarApp.getCalendarById(calId);
var recurrence = CalendarApp.newRecurrence().addYearlyRule();
for(var i = 0; i < data.length; i++) {
var title = data[i][1];
var date = new Date(data[i][0]);
var event = cal.createAllDayEventSeries(title, date, recurrence);
}
}
Also, if you wanted to delete previously created events when you create new events, you should keep track of all old events and edit this code a bit, but I'm not sure you want to delete them.
Update:
In case you want to create events when the sheet is edited, without having to run the function manually, I'd recommend using an onEdit trigger that creates an event corresponding to the row that has been written. Additionally, a condition can be added to create the event only if the data in the row is valid (columns A and B are not empty, and the value in column A is a valid Date).
The following function accomplishes all previous actions:
function createEvent(e) {
var sheet = e.source.getActiveSheet();
var range = e.range; // Edited range
var rowIndex = range.getRow(); // Edited row index
var firstCol = 1;
var numCols = 2;
var data = sheet.getRange(rowIndex, firstCol, 1, numCols).getValues()[0];
var title = data[1];
var date = data[0];
// Check whether column A is a valid Date and column B is not empty:
if(Object.prototype.toString.call(date) === '[object Date]' && title != "") {
var calId = sheet.getRange("E2").getValue(); // Get calendar id from cell 'E2'
var cal = CalendarApp.getCalendarById(calId);
var recurrence = CalendarApp.newRecurrence().addYearlyRule();
var event = cal.createAllDayEventSeries(title, date, recurrence); // Create event
}
}
In order to run on edit, this function needs an onEdit trigger. This trigger has to be installed, because a simple trigger cannot access services that require authorization.
You can install this trigger manually by following these steps (check this screenshot if you have problems when configuring the type of trigger).
You can also install this trigger programmatically, as explained here.
Please let me know if that works for you now. I hope this is of any help.

Update attendees when editing a Google calendar event with Google apps script

Situation
I have a calendar with a lot of events on it (staff appraisals).
I've made a load of modifications (changing the length of the event etc.) but the invitations are going to people who have a lotus notes calendar (poor people).
This means that unless I trigger what would be called "Send notification?" in the click-with-your-mouse version of things, they have no way of knowing that the event has been updated.
(Similar Q)
In this example, the event I'm trying to trigger is the same one as is triggered when the Send Update? modal is accepted with send.
Code
Here's some example code that gets all the events on the Appraisals calendar and changes their location to 'the moon'.
function fixInvitations(){
//get the callendar named "Appraisals"
var cApp = CalendarApp.getCalendarsByName("Appraisals")[0];
var events = cApp.getEvents(new Date(), new Date("Dec 30 2014"));
for (eIndex in events){
var event = events[eIndex];
event.setLocation("the moon");
}
}
Question
How do I trigger an update to all parties invited to an event so that the changes are reflected in their calendars?
Currently these events are now on the moon, but the update hasn't told people who are on non-Google calendars about the change.
Helpful, but not that helpful fact
The update email that manually triggering sends contains a .ics file (Gist of the contents). This contains a VCALENDAR and a VEVENT. From the Wikipedia page on VEVENTs
For sending an UPDATE for an event the UID should match the original UID. the other component property to be set is:
SEQUENCE:<Num of Update>
I.e., for the first update:
SEQUENCE:1
So if there was a way to manually build an email with a .ics attachment it would solve the problem, but that feels like massive overkill. This is mentioned here but not resolved.
The Google Calendar API supports the flag sendNotifications for event updates:
https://developers.google.com/google-apps/calendar/v3/reference/events/update
I would file a feature request for App Script to expose the flag and in the meantime use the Calendar API directly for updating events just like an external API:
https://developers.google.com/apps-script/guides/services/external
I was struggling with this one a bit and got some help from ekoleda+devrel#google.com: Ability for Calendar to send email invitation to users added via addGuest http://code.google.com/p/google-apps-script-issues/issues/detail?id=574
A couple of pointers to hopefully save people some time:
Note that CalendarApp and the Advanced Calendar service use different
event ID formats:
CalendarApp returns event IDs with "#google.com" at the end ex. b3gv...a5jrs#google.com
The Advanced Calendar service/Calendar API expects an event ID that does not have #google.com ex. b3gv...a5jrs
Here's some working code:
function sendInvite(calendarId, eventId, email) {
var event = Calendar.Events.get(calendarId, eventId);
if(event.attendees) {
event.attendees.push({
email: email
});
} else {
event.attendees = new Array({email: email});
}
event = Calendar.Events.patch(event, calendarId, eventId, {
sendNotifications: true
});
}
One final note for those following along - you won't receive an email notification if you're the event owner and you're trying to add your own email address. This makes sense in the calendar world since you wouldn't go into your own calendar event, add yourself as an attendee, and expect to be prompted as to whether or not you want to inform yourself. However, it's probably a common way for people to try and test things (I don't have a domain test account and I can't always reliably test things with my personal gmail account). So if you're testing with your own account and thinking that things aren't working please keep this in mind. Once you try things with a non-event owner account the invite will be sent via email.
I think one solution is to create a spreadsheet with the column headers: Event ID, Event Name, Description, Start Time, End Time, Location,Guest List, and then add this script to the spreadsheet. calendarImport() will import all of your calendar events and write them to the sheet. Then, you can modify the event in the spreadsheet line (change location to the Moon), and run calendarCreateEvent to create a new event based on the changes you made. It will automatically send out notifications to all old attendees to accept the new event, as it is a new invitation.
I am pretty close to that solution, but I have a hangup, now, on accessing the calendar to create a new event. I think it's because I'm calling it from an onEdit event function. It's getting late, but if you rewrite the third part of the script to get the events data range, then a for/if loop to check the added versus modified date, then you should be able to create a new event, and delete the old event. This would push a notification of the change in event details to everyone who had accepted the old event already.
I think, given the code example you put up in the beginning, you probably already know how to do that, but if you don't, I could probably throw something together tomorrow night to search for old events that have been recreated and delete them.
Here's my spreadsheet and code in action.
function calendarImport(){
//http://www.google.com/google-d-s/scripts/class_calendar.html#getEvents
// The code below will retrieve events between 2 dates for the user's default calendar and
// display the events the current spreadsheet
var cal = CalendarApp.getDefaultCalendar();
var calId = cal.getId();
var sheet = SpreadsheetApp.getActiveSheet();
var sheetName = SpreadsheetApp.getActiveSheet().setName(calId +" Calendar Data");
var events = cal.getEvents(new Date("March 9, 2014"), new Date("March 14, 2014"));
for (var i=0;i<events.length;i++) {
//http://www.google.com/google-d-s/scripts/class_calendarevent.html
Logger.log(events);
var details=[[events[i].getId(),events[i].getTitle(), events[i].getDescription(), events[i].getStartTime(), events[i].getEndTime(),events[i].getLocation()]];
var guestList = events[i].getGuestList();
var guestArray = [];
for (var n in guestList){
var guestEmail = (guestList[n].getEmail());
guestArray.push(guestEmail);
Logger.log(guestArray);
}
var row=i+1;
var range=sheet.getRange(row+1,1,1,6);
range.setValues(details);
var guestRange = sheet.getRange(row+1,7,1,1);
guestRange.setValues([guestArray]);
var dateAdded = Utilities.formatDate(new Date(), "GMT-6","MM/dd/yy HH:mm:ss");
var dateAddedRange = sheet.getRange(row+1,8,1,1);
dateAddedRange.setValue(dateAdded);
}
}
function onEdit(event){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var calId = CalendarApp.getDefaultCalendar().getId();
var sh = ss.getSheetByName(sheetName);
var actSht = event.source.getActiveSheet();
var actRng = event.source.getActiveRange();
var index = actRng.getRowIndex();
Logger.log(index);
var dateCol = actSht.getLastColumn();
var calId
var lastCell = actSht.getRange(index,dateCol);
var date = Utilities.formatDate(new Date(), "GMT-6", "MM/dd/yyyy HH:mm:ss");
lastCell.setValue(date);
var modifiedRow = sh.getRange(index,1,1,ss.getLastColumn()).getValues();
Logger.log(modifiedRow[0][7])
if (modifiedRow[0][7] < modifiedRow[0][8]){
var firstAdded = modifiedRow[0][7];
var dateModified = modifiedRow[0][8];
calendarCreateEvent(index,firstAdded,dateModified,calId);
}
}
function calendarCreateEvent(index,firstAdded,dateModified,calId){
var sheet = SpreadsheetApp.getActiveSheet();
var added = firstAdded;
var modified = dateModified;
var startRow = index; // First row of data to process
var calId = calId;
Logger.log(calId);
Logger.log(startRow);
Logger.log(added);
Logger.log(modified);
if (modified - added > "0"){
var numRows = 1; // Number of rows to process
var dataRange = sheet.getRange(startRow, 1, numRows, 9);
var data = dataRange.getValues();
var cal = CalendarApp.getCalendarById(calId);
for (i in data) {
var row = data[i];
var eventId = row[0]
var title = row[1]; // First column
var desc = row[2]; // Second column
var tstart = row[3];
var tstop = row[4];
var loc = row[5];
var guests = row[6];
//cal.createEvent(title, new Date("March 3, 2010 08:00:00"), new Date("March 3, 2010 09:00:00"), {description:desc,location:loc});
var newEvent = cal.createEvent(title, tstart, tstop, {description:desc,location:loc, guests:guests});//.addGuest(guests);
var newEventId = newEvent.getId();
Logger.log(newEventId);
}
}
}
I got an email from Gooogle today saying that they've added a feature to app script that makes this possible.
Updates:
Status: Fixed
Owner: ekoleda+devrel#google.com
Labels: -Priority-Medium
Comment #11 on issue 574 by ekoleda+devrel#google.com: Ability for Calendar to send email invitation to users added via addGuest
http://code.google.com/p/google-apps-script-issues/issues/detail?id=574
This is now possible using the Advanced Calendar service:
https://developers.google.com/apps-script/advanced/calendar
You can use Calendar.Events.patch() to add attendees, and if you set the optional parameter sendNotifications to "true" the attendees will get an invite.
https://developers.google.com/google-apps/calendar/v3/reference/events/patch
I'm going to try to solve this problem today and then edit this response to reflect that.