I am trying to write a Google Apps script to modify calendar events so I have modified an example to list events first. When I try debugging this it reports an error that "Calendar is not defined" on the line "events = Calendar.Events.list(calendarId, options);"
I have enabled the advanced calendar API, and am basing my script on one from the Google documentation, so I assume that one worked. Is there anything else I need to do to access the relevant objects and methods?
/*
Adapted from code in https://developers.google.com/apps-script/advanced/calendar
*/
function syncColourCode() {
var calendarId = CalendarApp.getDefaultCalendar();
var properties = PropertiesService.getUserProperties();
var fullSync = properties.getProperty('fullSync'); // sync status is stored in user properties
var options = {
maxResults: 100
};
var syncToken = properties.getProperty('syncToken'); // pointer token from last sync also stored in user properties
if (syncToken && !fullSync) { // if there is a sync token from last time and sync status has not been set to full sync
options.syncToken = syncToken; // adds the current sync token to the list of sync options
} else {
// Sync events from today onwards.
options.timeMin = new Date().toISOString(); //change to new Date().toISOString() from getRelativeDate(-1, 0).toISOString()
}
// Retrieve events one page at a time.
var events;
var pageToken;
do {
try {
options.pageToken = pageToken;
events = Calendar.Events.list(calendarId, options);
} catch (e) {
Not a google-apps expert, but from reviewing the code, I see a possible problem. At no point do I see your code checking to see if getDefaultCalendar() actually returned a valid calendar ID. Later your code uses that ID under the assumption that it is good. Have you checked the value of calendarId that is returned?
Sometimes you have to read a little deeper into the message, but I always try to start with trusting the error return. In this case "Calendar is not defined" makes me question the value of calendarId.
It seem that Google made some change so that there is no Calendar reference from the AppScript API.
Anyway to get the event you may use this API:
CalendarApp.getEvents(startTime, endTime)
https://developers.google.com/apps-script/reference/calendar/calendar-app#geteventsstarttime-endtime
Below are my example function running within google sheet.
function listEventsWithinTwoMonth(){
var calendar = CalendarApp.getDefaultCalendar();
var spreadsheet = SpreadsheetApp.getActiveSheet();
var now = new Date();
var twoMonthFromNow = new Date(now.getTime() + (24 * 60 * 60 * 30 * 4 * 1000));
var events = calendar.getEvents(now, twoMonthFromNow);
if (events.length > 0) {
// Header Rows
spreadsheet.appendRow(["#่","id","StartTime","EndTime","Title","Description"]);
for (i = 0; i < events.length; i++) {
var event = events[i];
Logger.log("" + (i+1) + event.getId() +" "+ event.getStartTime()+" "+event.getEndTime()+" "+event.getTitle()+" "+event.getDescription())
spreadsheet.appendRow([(i+1),event.getId(),event.getStartTime(),event.getEndTime(),event.getTitle(),event.getDescription()]);
}
} else {
Logger.log('No upcoming events found.');
}
}
Hope this help.
CalendarApp.getDefaultCalendar() retrieves an object of the class Calendar that has multiple properties, among others an Id.
To retrieve the calendar Id, you need to define
var calendarId = CalendarApp.getDefaultCalendar().getId();
Related
I want to script behavior when people insert event in my calendar.
(e.g. when they add something into my "focus time"). I was able to connect an appscript "trigger" to call onEventUpdate. Sadly AppScript does not give you the event id for the event that was modified... (can you confirm the API does not offer this?).
So I tried to fetch the "last updated" events instead:
function getOptions() {
var now = new Date();
var yesterday = new Date();
yesterday.setDate(now.getDate() - 1);
console.log(yesterday.toISOString())
return {
updateMin: yesterday.toISOString(),
maxResults: 2,
orderBy: 'updated',
singleEvents: true,
showDeleted: false
}
}
function onEventUpdate() {
var options = getOptions();
var calendarId = Session.getActiveUser().getEmail();
// var calendar = CalendarApp.getCalendarById(calendarId);
// console.log(calendar.getName());
var events = Calendar.Events.list(calendarId, options);
if(!events.items) return
for (var i = 0; i < events.items.length; i++) {
var event = events.items[i];
console.log(event.summary + " # " + event.start['dateTime']);
}
}
Yet, I have just modified an event, but instead I am getting events from the past (i.e. August, 2mo ago...):
5:11:21 PM Notice Execution started
5:11:21 PM Info 2022-10-29T00:11:21.826Z
5:11:22 PM Info Old Meeting # 2022-08-08T17:00:00-07:00
5:11:22 PM Info Old Meeting # 2022-08-03T14:00:00-07:00
5:11:22 PM Notice Execution completed
Thoughts?
I believe your goal is as follows.
You want to retrieve the last updated event in a Google Calendar using Google Apps Script.
In the current stage, orderBy of "Events: list" can be used with only "ascending". When I saw your script, I thought that the reason for your current issue might be due to this. If "descending" was used with orderBy, your script might be able to be used. But, in the current stage, it seems that this cannot be used.
So, in the current stage, in order to retrieve the last updated event, I thought that it is required to retrieve all events with your getOptions(), and the last element is required to be retrieved. When this is reflected in your script, how about the following modification?
Modified script:
function getOptions() {
var now = new Date();
var yesterday = new Date();
yesterday.setDate(now.getDate() - 1);
console.log(yesterday.toISOString())
return {
updatedMin: yesterday.toISOString(),
maxResults: 2500, // Modified
orderBy: 'updated',
singleEvents: true,
showDeleted: false
}
}
function onEventUpdate() {
var options = getOptions();
var calendarId = Session.getActiveUser().getEmail();
// I modified the below script.
var eventList = [];
var pageToken = null;
do {
options.pageToken = pageToken;
var events = Calendar.Events.list(calendarId, options);
if (events.items.length > 0) {
eventList = [...eventList, ...events.items];
}
pageToken = events.nextPageToken;
} while (pageToken);
var lastUpdatedEvent = eventList.pop(); // You can retrieve the last updated event.
var lastUpdatedEventId = lastUpdatedEvent.id; // You can retrieve the event ID of the last updated event.
}
Note:
About I was able to connect an appscript "trigger" to call onEventUpdate. Sadly AppScript does not give you the event id for the event that was modified, how about reporting this to the Google issue tracker as a future request?
Reference:
Events: list
Alright, I achieved what I wanted to (thanks for spotting my typo). I take some risk, as I assume there is no more than 50 edited events in the last hour (although risk could be reduced by making this delta smaller).
This is what the overly-convoluted way appscript needs (i.e. they could have just given me a calendar-event object as an argument to the trigger? oh well)
This is just a simple example, as now I can programmatically edit my calendar at will :)
function getOptions() {
var now = new Date();
TIME_DIFF = 60 * 60 * 1000;
var earlier = new Date(now.getTime() - TIME_DIFF)
return {
updatedMin: earlier.toISOString(),
maxResults: 50,
orderBy: 'updated',
singleEvents: true,
showDeleted: false
}
}
function getLastEditedEvent() {
// returns https://developers.google.com/apps-script/reference/calendar/calendar-event
var options = getOptions();
var calendarId = Session.getActiveUser().getEmail();
var events = Calendar.Events.list(calendarId, options);
if(!events.items) return undefined;
var _event = events.items[events.items.length-1];
return CalendarApp.getEventById(_event.id);
}
function onEventUpdate() {
// sadly event update contains no information
var event = getLastEditedEvent()
if(event == undefined) return;
console.log('Modifying: ' + event.getTitle() + ' # ' + event.getStartTime());
event.setColor(CalendarApp.EventColor.PALE_BLUE);
}
I have been puzzling this over for some time and have searched extensively, but found no solution.
I'm using Google Apps Script and I run events for a large organization and we have about 80 different registration Google Forms for these events. I want to get the registrant's email address and send them an email when they submit their form. This is easy to accomplish by setting up each individual form. Ideally, I would set up the onSubmit trigger for the form and then copy that form for each new event. However, it seems you cannot install a trigger programmatically without going to the form and running the script manually, and then authorize it. When a form is copied it also loses all its triggers. Am I wrong about this? Doing this for each form is not realistic given that events are added all the time and I have other responsibilities.
Is there no way to set a trigger for these files without running and authorizing each one?
My other solution is:
I am trying to get all the forms in a folder and then get the responses and send a single email to each registrant. This seems overly complicated and requires checking all the forms regularly since there are no triggers for the individual forms. I tried setting triggers in my spreadsheet for the forms and this works, but the number of triggers for a spreadsheet is limited to 20, so doesn't work here. Running my script every minute and then checking if an email has been sent to each respondent seems possible, but complex and possibly prone to errors...
Thanks for any help you can offer!
pgSystemTester's answer worked for me.
I added two bits of code.
One, to declare the time stamp value to zero if there wasn't one
there.
Two, the code needed a "-1" when you get dRange or you insert a new
row which each run.
function sendEmailsCalendarInvite() {
const dRange = sheet.getRange(2, registrationFormIdId, sheet.getLastRow() - 1, 2);
var theList = dRange.getValues();
for (i = 0; i < theList.length; i++) {
if (theList [i][1] == ''){
theList[i][1] = 0;
}
if (theList[i][0] != '') {
var aForm = FormApp.openById(theList[i][0]);
var latestReply = theList[i][1];
var allResponses = aForm.getResponses();
for (var r = 0; r < allResponses.length; r++) {
var aResponse = allResponses[r];
var rTime = aResponse.getTimestamp();
if (rTime > theList[i][1]) {
//run procedure on response using aForm and aResponse variables
console.log('If ran')
if (rTime > latestReply) {
//updates latest timestamp if needed
latestReply = rTime;
}
//next reply
}
}
theList[i][1] = latestReply;
//next form
}
}
//updates timestamps
dRange.setValues(theList);
}
This is probably a simple solution that will work. Setup a spreadsheet that holds all Form ID's you wish to check and then a corresponding latest response. Then set this below trigger to run every ten minutes or so.
const ss = SpreadsheetApp.getActiveSheet();
const dRange = ss.getRange(2,1,ss.getLastRow(),2 );
function loopFormsOnSheet() {
var theList = dRange.getValues();
for(i=0;i<theList.length;i++){
if(theList[i][0]!=''){
var aForm = FormApp.openById(theList[i][0]);
var latestReply = theList[i][1];
var allResponses = aForm.getResponses();
for(var r=0;r<allResponses.length;r++){
var aResponse = allResponses[r];
var rTime = aResponse.getTimestamp();
if(rTime > theList[i][1]){
//run procedure on response using aForm and aResponse variables
if(rTime >latestReply){
//updates latest timestamp if needed
latestReply=rTime;
}
//next reply
}
}
theList[i][1] = latestReply;
//next form
}
}
//updates timestamps
dRange.setValues(theList);
}
I need to create an event in everyone's calendar of my organization. This event is a reminder of an action everyone needs to take before a certain date, but this date is not the same every month, so I can't do a recurring event. I don't want to add everyone as attendee because I want everyone keep the event on the calendar and not just answer "don't participate" and forgot to do what they need to do at the good time.
To do this, I already wrote a code that gets every active account from the organisation, gets the start date, end date, event title and event description from a google sheet. It works for my calendar and calendars of people I add manually on my list "other calendars" in google calendar. But the script doesn't create the event in agendas of people in my organisation not present on my "other calendar" list.
function getAllPeopleFromDirectory() {
var pageToken;
var page;
var usersId = [];
do {
page = AdminDirectory.Users.list({
domain:'mydomain.com',
orderBy: 'givenName',
maxResults: 400,
query: "orgUnitPath=/ isSuspended=False",
pageToken: pageToken
});
var users = page.users;
if(users) {
for (var i=0; i< users.length; i++) {
var user = users[i];
usersId.push(user.primaryEmail);
}
} else {
Logger.log('No users found.');
}
pageToken = page.nextPageToken;
} while(pageToken);
if (usersId.length != 0){
setUpReminder(usersId);
}
}
function setUpReminder(calendarIDs) {
var ss = SpreadsheetApp.openById("SPREADSHEET_ID");
var sheet = ss.getSheetByName('reminder');
var range = sheet.getDataRange();
var values = range.getValues();
for (var i = 0; i < calendarIDs.length; i++) {
setUpCalendar(values, range, calendarIDs[i]);
}
}
function setUpCalendar(values, range, calendarId) {
var cal = CalendarApp.getCalendarById(calendarId);
for (var i = 1; i < values.length; i++) {
var session = values[i];
var date = new Date(session[1]);
var now = new Date();
if (date.getMonth() == now.getMonth()) {
var title = session[4];
var start = joinDateAndTime(session[1], session[2]);
var end = joinDateAndTime(session[1], session[3]);
var options = {location: session[5], sendInvites: false, description: session[8]};
var event = cal.createEvent(title, start, end, options).setGuestsCanSeeGuests(true);
}
}
range.setValues(values);
}
function joinDateAndTime(date, time) {
date = new Date(date);
date.setHours(time.getHours());
date.setMinutes(time.getMinutes());
return date;
}
When I call "CalendarApp.getCalendarById" with everyone who isn't on my other calendars, the function returns null so cal.createEvent raises an error...
Does anyone have an idea how to make this correctly?
Maybe I can add and remove people to my other calendar every time I run the script, it's not the best solution but if someone knows how to do this, it would be cool!
Instead of fetching user-specific (primary) calendars and updating them with events one-by-one, a better approach is to create a custom calendar (aka. a secondary calendar) that's shared among all the users under your GSuite domain.
Here's an excerpt from the Calendar API reference documentation:
...you can explicitly create any number of other calendars; these calendars can be modified, deleted, and shared among multiple users.
So, all you need do is update the secondary calendar and its events will be reflected in your users' calendar view (but only if your users have the custom calendar enabled).
See Primary Calendars and other calendars
See Sharing Calendars.
I have a very basic problem within my whole code which became a head-ache for a couple of days. I have made an extensive research on this issue however couldn't be able to find an exact solution (or I have missed the one's that I might have found). Here it is:
I have a spreadsheet, where I log in Google Calendar events with their properties including the id of the event. Sometimes, I clean up the Google Calendar manually and just want to run a code which checks the events existence in the calendar and if not delete the row. My code is:
function cleanCal(calid, eventid) {
var calid = 'my calendar id';
var eventid = 'event id';
var event = CalendarApp.getOwnedCalendarById(calid).getEventById(eventid);
if (event) {
event.deleteEvent();
// clean-up sheet
} else {
// clean-up sheet
}
}
Basically if the event is in the calendar, the code shall delete it first and then clean the sheet, if it's not there, it should clean the sheet only. However, when the code is executed, though the calendar event is not there, the if statement returns true and raises an error when trying to delete the event as it's actually not there. I haven't been able to find out the reason, how and why the event object return true though the event is not existing. Where am I doing wrong?? Thanks for replying and any help is really appreciated.
[EDIT]
This is the code that I use to check events existence with Calendar API v3
function verifyCalendarEvent_OLD(calid, eventid) {
var cal = CalendarApp.getCalendarById(calid)
var exists = true;
var response = Calendar.Events.list(
calid, {
showDeleted: true,
fields: "items(id,status,summary)"
}
);
for (var i = 0; i < response.items.length; i++) {
if (response.items[i].id == eventid.split("#")[0] && response.items[i].status == "cancelled") {
exists = false;
break;
}
}
return exists;
}
How about this answer?
Modification points :
For example, if there is no event of the event ID, CalendarApp.getOwnedCalendarById(calid).getEventById(eventid) returns null. When this is evaluated by if, that is used as false.
So I thought that you might try to retrieve the event which has already been removed. Because at Google Calendar, even if the event is removed, the event ID is remained. So although there is no event for the event ID, if (event) {} in your script returns true. I confirmed that CalendarApp.getOwnedCalendarById(calid).getEventById(eventid) retrieves the removed events. For this situation, you can know whether the event is removed by confirming the event status.
When the event status is confirmed, it indicates that the event is still not removed.
When the event status is cancelled, it indicates that the event has already been removed.
Preparation to use this modified sample script :
When you use this modified script, please enable Calendar API at Advanced Google Services and API console.
Enable Calendar API v3 at Advanced Google Services
On script editor
Resources -> Advanced Google Services
Turn on Calendar API v3
Enable Calendar API at API console
On script editor
Resources -> Cloud Platform project
View API console
At Getting started, click Enable APIs and get credentials like keys.
At left side, click Library.
At Search for APIs & services, input "Calendar". And click Calendar API.
Click Enable button.
If API has already been enabled, please don't turn off.
When you run this script, if an error occurs, you might be required to wait few minutes until the API is enabled.
Modified script :
function cleanCal(calid, eventid) {
var calid = 'my calendar id';
var eventid = 'event id';
var status = Calendar.Events.get(calid, eventid).status; // Added
if (status == "confirmed") { // Modified
var event = CalendarApp.getOwnedCalendarById(calid).getEventById(eventid); // Added
event.deleteEvent();
// clean-up sheet
} else {
// clean-up sheet
}
}
If I misunderstand your question, I'm sorry.
Edit :
In my environment, the events removed by manual and script can be retrieved as status=cancelled. Since I didn't know your situation, I prepared a sample script. This sample script is a simple flow.
Create new event.
Delete the created event.
Confirm the deleted event.
Here, your additional script was used.
Sample script :
function deleteeventa() {
// Create new event
var calid = 'my calendar id';
var c = CalendarApp.getCalendarById(calid);
var r = c.createEvent("sample event for deleting", new Date(2018,0,11,00,00), new Date(2018,0,11,01,00));
// Delete the created new event
var eventid = r.getId().split('#')[0];
var event = c.getEventById(eventid);
event.deleteEvent();
// Your additional script
var exists = true;
var response = Calendar.Events.list(
calid, {
showDeleted: true,
fields: "items(id,status,summary)"
}
);
for (var i = 0; i < response.items.length; i++) {
if (response.items[i].id == eventid && response.items[i].status == "cancelled") {
exists = false;
break;
}
}
Logger.log("%s, %s, %s", r.getTitle(), r.getId(), exists)
}
Result :
sample event for deleting, ######google.com, false
Following Tanaike's great help, I have come up with this code snippet which - as far as I have tested - works fine for now. Hope it might be helpful for other users. Here is the code fragment:
function verifyCalendarEvent(calid, eventid) {
var cal = CalendarApp.getCalendarById(calid)
eventid = eventid.split("#")[0];
var exists = true;
var eventIds = [];
var eventStats = [];
var response = Calendar.Events.list(
calid, {
showDeleted: true,
fields: "items(id,status,summary)"
}
);
for (var i = 0; i < response.items.length; i++) {
eventIds.push(response.items[i].id);
}
for (var i = 0; i < response.items.length; i++) {
eventStats.push(response.items[i].status);
}
if (eventIds.indexOf(eventid) > 0) {
for (var i = 0; i < eventIds.length; i++) {
if (eventIds[i] == eventid && eventStats[i] == "cancelled") {
exists = false;
}
}
} else {
exists = false;
}
Logger.log("Calendar Event ["+eventid+"] exists? >> "+exists);
return exists;
}
I have a google script with the following code:
function hoursBetween(fromDate, toDate) {
var dates = initializeDates();
var from = dates[fromDate];
var to = dates[toDate];
var workCals = CalendarApp.getCalendarsByName('Work');
var workCal = workCals[0];
var events = workCal.getEvents(from, to);
var predictedMonthlyHours = 0;
for (var i = 0; i < events.length; i++) {
var event = events[i];
var startTime = event.getStartTime();
var endTime = event.getEndTime();
var duration = endTime - startTime;
duration = parseFloat(duration/(3600*1000));
duration -= dates['break'];
predictedMonthlyHours += duration;
}
return predictedMonthlyHours;
}
I have ran in the script editor with no problems (it asked for auth and I accepted)
When I try to call the function from my spreadsheet I get the following error:
"You do not have permission to call getCalendarsByName"
I have also tried adding triggers with no result.
How are you trying to call from spreadsheet?
If you're trying to call via a spreadsheet formula =hoursBetween(), then it won't work. See Custom Functions..."Unlike most other types of Apps Scripts, custom functions never ask users to authorize access to personal data. Consequently, they can only call services that do not have access to personal data,"
Other options to set the cell value:
manual button click in a sidebar Add-on
manual click on a custom menu option
install a timed trigger to run automatically
Because the method is looking for multiple "Calendars" it's returning an array of calendars. So if you have only one calendar named "Work", you still need to call it and pick which one it is. So try changing the variable "workCals" to `
var workCals = CalendarApp.getCalendarsByName('Work')[0];
Adding the [0] will pick the first one available.
With out seeing the rest of the code, hard to decipher.