Copy a programmatically created recurring event? - google-apps-script

Using Google Apps Script, I created a calendar that has a complex recurring event. However, I'm not able to copy that event to another calendar as a recurring event. Note: the recurring rule can't be generated or edited via the web interface.
For a real example, let's say a user wants to copy the recurring event from this publicly shared Google calendar. The event is a kind of template event for a course schedule of a Thursday course at my university.
Here are two difficulties preventing the user from copying the recurring event into another calendar the user has access to:
clicking on an instance of the event and saying "copy to my calendar" only copies that single event, even though the event is defined as repeating. This is not a good solution, since the user would have to do this more than 10 times to get all the events for a semester.
clicking on an instance of the event and then "More details" , then trying to "Copy to xxxx" under the "More actions" menu (where xxxx is a calendar the user owns) appears to work. However, when clicking Save, there is an error: "An error has occurred. Please try again later."
EDIT here's a screen shot of what the Event looks like in Google Calendar (click on event, "more details").
Note that this recurring event is every Monday from the start of the semester to the end, with several exceptions. No holidays (e.g., Mon 2013-02-25), but also including Wed 2013-02-27, on which day the courses will be given as if it were a Monday (according to the university course schedule).
I repeat: the recurring events look fine in Google Calendar, but they can't be copied in there entirety to another calendar.
The GAS function that creates the calendars (not all the code is here):
function createCalendar(calendarName, courseCode, weekday, times, room, isLab) {
Logger.log("Last day: " + LAST_TRIMESTER_DATE);
// hack the last day so that it's not midnight but rather just before the next day, i.e., 23:59:59
var adjustedLastDay = LAST_TRIMESTER_DATE;
adjustedLastDay.setTime(adjustedLastDay.getTime() + (23*60*60*1000) + (59*60*1000) + (59*1000));
Logger.log("Adjusted last day: " + adjustedLastDay);
var eventRecurrence = CalendarApp.newRecurrence();
eventRecurrence.addDailyRule().until(adjustedLastDay).interval(1).onlyOnWeekday(weekday);
// get the day of the week of the first day
var weekdayOfFirstDay = Utilities.formatDate(FIRST_TRIMESTER_DATE, LONG_TIME_ZONE, "EEEE").toUpperCase();
// if this calendar is for a lab, exclude the first week of days
if (isLab) {
eventRecurrence.addDailyExclusion().times(1);
} else {
// it's a course, so exclude the first day if it's not the course day
// -- this is kind of a bug, since the "first day" of the event will always try to be created, even if it's not "onlyOnWeekday" specified
if (weekdayOfFirstDay != weekday.toString()) {
eventRecurrence.addDailyExclusion().times(1);
// eventRecurrence.addDateExclusion(FIRST_TRIMESTER_DATE);
}
}
// Exclude all holidays
for (var i = 0; i

This appears to be a bug in Google Calendar, in that it has trouble copying complex recurrence rules. You can report it to the team by using the "Send feedback" link under the gear icon in the Google Calendar interface.

The other option would be to try and export the ICA and have the user import it. You might be even able to do both with some sort of link button. Here is an example of how to obtain the ica file for a google event;
http://google.com/calendar/ical/[CALENDARID]/public[or private depending]/full/[eventID].ics
This should give the code to the user and they can manually import it into their calendars. You can generate the url and send in an email to all students who sign up for the named course.
T

Related

How can I receive the current displayed week of google calendar with app-script?

I cannot find any property receiving the currently for the user displayed calendar week with google apps-script here
What I want to do is:
Extract the start and end date of the days displayed for the user in google-calendar.
What I already archive is to get events of the current week's Monday (so from the perspective of today):
getEventsOfThisWeek(calendarId) {
calendar = CalendarApp.getCalendarById(calendarId);
let referenceDay = new Date(); //here I would like to get a day from displayed week
let monday = this.getMondayMidnightAM(referenceDay);
let friday = theĆ­s.getFridayMidnightPM(referenceDay)
let events = calendar.getEvents(monday, friday );
return events
}
getMondayMidnightAM(ref) {
let day = ref.getDay();
let diff = ref.getDate() - day + (day == 0 ? -6 : 1); // adjust when day is sunday
ref.setDate(diff);
let mondayMidnight = new Date(ref.toISOString().replace(/T\S{8}/, "T00:00:00"));
return mondayMidnight;
}
So far I understood, that isSelected() tells me not if the event is on screen in the current view, but rather if the display in all the calendar option is on.
In Short: It is not possible.
The problem with the calendar page you see as a user is that if multiple users have access to the same (e.g. group) calendar, every user might look at at different week.
The script can only access server-side data that is the same for every user (e.g. event name, guests etc.).
Only an Addon can access some specific information that only the user who installed the Addon sees.
This information is contained in object events that become available when a manifest or contextual trigger get fired - e.g. when a user creates or edits an event
However, as you can see in the documentation the Calendar Event Objects do not comprise any information about the currently open week, so there is no way to get this information programamtically
Maybe it is possible to get this data through webscraping, whereby I would not be able to tell you if and how this can be done

How do I construct a link to a Google Calendar event?

If I have the event ID of a Google Calendar event, for example:
4htgpmm1hmak744r0kbkdcodar#google.com
How can I construct a URL to view the event details in the Google Calendar interface?
For example, if I go to a google calendar event, I see a URL such as this:
https://calendar.google.com/calendar/r/eventedit/NGh0Z3BtbTFobWFrNzQ0cjBrYmtkY29kYXIgZXVndTlrYW4xZGRpMXBtaTZzazNpYjWoNmdAZw
What function/algorithm can I run to go from 4htgpmm1hmak744r0kbkdcodar#google.com to NGh0Z3BtbTFobWFrNzQ0cjBrYmtkY29kYXIgZXVndTlrYW4xZGRpMXBtaTZzazNpYjWoNmdAZw?
Bonus: A bonus would be if there was also a shortcut method that worked on a CalendarEvent via Google Apps Script. For example, if there was a method such as getUrl() just like there is a getId() method.
Since the same event can exist in multiple calendars (e.g. by inviting others to it), the event id is not sufficient information.
To construct a URL you need both the event id (refresher for how find it), AND the Calendar ID (see below for instructions as to how to find it). You'll then need to encode both as base64, and finally you'll you have a valid Google Calendar event link.
For the following instructions, imagine we have an event, Event1, and it exists in both calendars, CalendarA and CalendarB.
In the Google Calendar interface, you would be able to view any of these combinations:
Event1 in CalendarA
Event1 in CalendarB
Let's assign the following IDs:
Event1 ID: 4htgpmm1hmak744r0kbkdcodar#google.com
CalendarA ID: eugu9kan1ddi1pmi6sk3ib56g#group.calendar.google.com
CalendarB ID: afd3zeuguoepmi32s5i56g#group.calendar.google.com
To construct an event URL:
Get the event-id, which is the event id without the #google.com
E.g. for Event1 we have 4htgpmm1hmak744r0kbkdcodar
Get the calendar-id, which is the calendar id with the #group.calendar.google.com replaced with #g
E.g. for CalendarA we have eugu9kan1ddi1pmi6sk3ib56g#g
Join the two values with a space in the middle.
<event-id> <calendar-id>
E.g. for Event1 + CalendarA, we have:
4htgpmm1hmak744r0kbkdcodar eugu9kan1ddi1pmi6sk3ib56g#g
Encode the value as base64 (e.g. on https://www.base64decode.org):
NGh0Z3BtbTFobWFrNzQ0cjBrYmtkY29kYXIgZXVndTlrYW4xZGRpMXBtaTZzazNpYjWoNmdAZw==
Remove the training ==:
NGh0Z3BtbTFobWFrNzQ0cjBrYmtkY29kYXIgZXVndTlrYW4xZGRpMXBtaTZzazNpYjWoNmdAZw
You can now append this value to https://www.google.com/calendar/event?eid= (view event page) or https://calendar.google.com/calendar/r/eventedit/ (edit event page):
https://www.google.com/calendar/event?eid=NGh0Z3BtbTFobWFrNzQ0cjBrYmtkY29kYXIgZXVndTlrYW4xZGRpMXBtaTZzazNpYjWoNmdAZw
or
https://calendar.google.com/calendar/r/eventedit/NGh0Z3BtbTFobWFrNzQ0cjBrYmtkY29kYXIgZXVndTlrYW4xZGRpMXBtaTZzazNpYjWoNmdAZw
You're done! That URL will take you to the calendar event in the Google interface.
How to find the calendar ID
If you need instructions for how to find the calendar id...
To find the calendar ID:
Go to the calendar settings
Scroll down until you find the calendar id field.
This might be a value that ends with #group.calendar.google.com, your email address, or some other value.
If you are having trouble finding the calendar id, another tool that might help is adding the eventdeb=1 parameter to the URL, going to the Troubleshooting info of an event, and then looking for either the organizer or participant values, both of which contain calendar ids.
In Apps Script
var eventUrl = "https://www.google.com/calendar/event?eid=" +
Utilities.base64Encode(event.getId().split('#')[0] +
" " +
event.getOriginalCalendarId())
.replace(/\=/g, '');
#Senseful had was a super helpful answer, and it works!
A few important things to note adding to the above. Firstly, when base64 encoding, you'll end up with a == at the end, remove that
Second, the base calendar URL will not work when the calendar the event belongs to does not belong in the primary account the user is logged in with. For example, if you're logged into 3 accounts, you'll have a different URL for each one e.g.
https://calendar.google.com/calendar/r/...
https://calendar.google.com/calendar/b/2/r/...
https://calendar.google.com/calendar/b/3/r/...
and so on... I don't think there's a solution that'll help with this situation since it's impossible to predict what accounts a specific user is logged into at any time.

Google Calendar Appointment Slot : Custom Functionality

I want to use Google Calendar for a 3D Scanner appointment booking functionality. First, I would choose a appointment slot to make available for customers to book appointments, then I would take the link for the slot and make it public to the potential customers. Afterwards, different people can book appointments for the slots, however the slot is only filled when the customer comes in and pays for the scanner service in our office on a first come first serve bases.
My problem with this scenario in Google Calendar is that once the first customer books the appointment, the slot appear filled on the calendar such as below:
I don't want this, I want the slot to still be open to other customers but when the first customer books an appointment, I want to send him an email saying come to our office and pay as soon as possible for the time slot to be yours.
So now that I have described what I want, I want to investigate is this functionality possible in Google Calendar? Currently I have to manually click on the invitation email Going to? "No/Maybe" for the slot to become available for others :
Can I control this Going to option/property via app script? Is there an event handler for when appointment is booked? Any help is appreciated.
Update
function calendarTest1() {
// Gets the public calendar "US Holidays" by ID.
var calendar = CalendarApp.getCalendarById('id_usually_email');
//Logger.log('The calendar is named "%s".', calendar.getName());
//Logger.log('The calendar description is "%s".', calendar.getDescription());
var today = new Date();
var events = calendar.getEventsForDay(today);
//Logger.log('Number of events: ' + events.length);
for(event in events ){
Logger.log('Title: ' + events[event].getTitle());
Logger.log('Start Time: ' + events[event].getStartTime().toISOString());
Logger.log('My Status: ' + events[event].getMyStatus());
Logger.log('Id: ' + events[event].getId());
events[event].setMyStatus(CalendarApp.GuestStatus.NO);
Logger.log('My Status: ' + events[event].getMyStatus());
Logger.log('Guest List: ' + events[event].getGuestList()[0].getEmail());
var guests = events[event].getGuestList();
Logger.log('----------------------------------------------------------------');
}
}
As per https://developers.google.com/apps-script/reference/calendar/event-guest#getGuestStatus(), there is a getGuestStatus() function, I need to know if there is a setGuestStatus() so I may change the status from "yes" to "no".
Update
So it finally worked, it turned out that you create a appointment slot its not an event or anything, but when someone comes a book on the appointment it is just a regular event then, so you just fetch that event and turn myStatus() yes -> no and it should do the trick.
Google Calender: Modify Event Guest Status

Google Sheet to Calendar script for creating editorial calendar / changing variable names

I would like to have a Google Sheet that syncs with a Google Calendar to help keep my editorial calendar more organized. I've found some great code online that does 90% of what I need, but the code is primarily for event management. All of my entries will be all-day events, so I don't really need the start and end time feature.
Here is what I would like to accomplish:
Rename the 'Start Time' column to 'Date'
Delete the 'End Time' column
Default all entries to all-day events
The original developer was kind enough to include a spreadsheet template.
I tried changing the name of column C on the spreadsheet to Date and then updating var titleRow in row 10, but I get the following error message:
Spreadsheet must have Title, Start Time, End Time, All Day Event, and Id columns
Any suggestions on how to correct the error? What about defaulting to all-day events? Any advice is greatly appreciated!
Try using the Calendar Service
createAllDayEvent(title, date)
Creates a new all-day event.
createAllDayEvent(title, date, options)
Creates a new all-day event.(with options)
createAllDayEventSeries(title, startDate, recurrence)
Creates a new all-day event series.
createAllDayEventSeries(title, startDate, recurrence, options)
Creates a new all-day event series.(with options)
Code sample for all-day events series(with options)
// Creates an event series for a no-meetings day, taking place every Wednesday in 2013.
var eventSeries = CalendarApp.getDefaultCalendar().createAllDayEventSeries('No Meetings',
new Date('January 2, 2013 03:00:00 PM EST'),
CalendarApp.newRecurrence().addWeeklyRule()
.onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)
.until(new Date('January 1, 2014')),
{guests: 'everyone#example.com'});
Logger.log('Event Series ID: ' + eventSeries.getId());
Creating an event as a all day event, we use createAllDayEvent / createAllDayEventSeries function. For the series it will use the recurrence and some additional parameters like location, description, guest, sendInvites.
Hope this helps

How to create AllDay Event with Google Apps Script that span multiple days and show as a single bar

I have a script that takes event information for google sheet and adds them into a specified google calendar. So far everything works fine except when I try to create an all day event that spans multiple days. The scenario that I am fighting with is for vacation. If I create the event manually, I can choose "All day" and set a start and end date of two different days. Note, "Repeat..." is not used. Visually the event spans the days specified. This is what I want to duplicate with my script.
As far as I can tell, the API only provides:
calendar.createEvent(title, startTime, endTime)
which creates a non-all day event that does span days, but it shows the start time which is not what I want. The event is truly and all day event.
calendar.createAllDayEvent(title, date)
which creates and all day event for only one day.
What seems to be missing is
calendar.createAllDayEvent(title, startDate, endDate)
which is what is discussed here: https://code.google.com/p/google-apps-script-issues/issues/detail?id=952, but there isn't any significant movement on the issue.
A possible work around is discussed here: How to create two days AllDay Event with Google Apps Script?. The problem is creates what looks like multiple single all day events. I want the multi day to show up as spanning multiple days.
Has anyone found any other work around?
You can use createEventFromDescription(), it works perfectly for "n" full days.
var start = Utilities.formatDate(new Date(date_from), "GMT"-0500, "MM-dd-yyyy");
var end = Utilities.formatDate(new Date(date_to), "GMT"-0500, "MM-dd-yyyy");
var vacationCalendar = CalendarApp.getCalendarById(CALENDAR_ID);
vacationCalendar.createEventFromDescription(title+" "+start+"-"+end);
Edy's suggestion of createEventFromDescription() is nice and clean, +1.
With that being said, if you need more fine-grained control over the event and have a lot of metadata you're trying to set (such as location, description, attendees, etc), I would recommend using the new version of the Google Cal API aka "Advanced Calendar Service", which natively supports multiple-day all-day events.
Basically looks like this:
// end dates are exclusive, so this creates a 2-day event
// starting on the 9th and ending on the 10th
var event = {
summary: 'My Event',
start: {
date: '2015-04-09' // unfortunate it doesn't seem to accept JS Date() objects :(
},
end: {
date: '2015-04-11'
}
};
event = Calendar.Events.insert(event, calId);
For what it's worth, "use this new API" is also the answer given by a google dev on the issue ticket mentioned in the original description of this StackOverflow post.