Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 73 additions & 69 deletions SyncCalendarsIntoOne.gs
Original file line number Diff line number Diff line change
Expand Up @@ -34,36 +34,12 @@ function SyncCalendarsIntoOne() {
endTime.setHours(0, 0, 0, 0);
endTime.setDate(endTime.getDate() + SYNC_DAYS_IN_FUTURE + 1);

// Delete old events
const deleteStartTime = new Date();
deleteStartTime.setFullYear(2000, 01, 01);
deleteStartTime.setHours(0, 0, 0, 0);

deleteEvents(deleteStartTime, endTime);
createEvents(startTime, endTime);
syncEvents(startTime, endTime);
}

// Delete any old events that have been already cloned over.
// This is basically a sync w/o finding and updating. Just deleted and recreate.
function deleteEvents(startTime, endTime) {
const sharedCalendar = CalendarApp.getCalendarById(CALENDAR_TO_MERGE_INTO);

// Find events with the search character in the title.
// The `.filter` method is used since the getEvents method seems to return all events at the moment. It's a safety check.
const events = sharedCalendar
.getEvents(startTime, endTime, { search: SEARCH_CHARACTER })
.filter((event) => event.getTitle().includes(SEARCH_CHARACTER));

const requestBody = events.map((e, i) => ({
method: 'DELETE',
endpoint: `${ENDPOINT_BASE}/${CALENDAR_TO_MERGE_INTO}/events/${e
.getId()
.replace('@google.com', '')}`,
}));

function submitRequest(requestBody, action){
if (requestBody && requestBody.length) {
const result = new BatchRequest({
useFetchAll: true,
batchPath: 'batch/calendar/v3',
requests: requestBody,
});
Expand All @@ -72,69 +48,97 @@ function deleteEvents(startTime, endTime) {
console.log(result);
}

console.log(`${result.length} deleted events.`);
console.log(`${result.length} events ${action}d.`);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo ${action}d.

} else {
console.log('No events to delete.');
console.log(`No events to ${action}.`);
}
}

function createEvents(startTime, endTime) {
let requestBody = [];
function syncEvents(startTime, endTime) {

let requestBody_delete = [];

// get target calendar events
const target_events = Calendar.Events.list(CALENDAR_TO_MERGE_INTO, {
timeMin: startTime.toISOString(),
timeMax: endTime.toISOString(),
singleEvents: true,
orderBy: 'startTime',
}).items.filter((event) => event.summary.includes(SEARCH_CHARACTER));

for (let calendarName in CALENDARS_TO_MERGE) {

// get source calendar events
const calendarId = CALENDARS_TO_MERGE[calendarName];
const calendarToCopy = CalendarApp.getCalendarById(calendarId);

if (!calendarToCopy) {
if (!CalendarApp.getCalendarById(calendarId)) {
console.log("Calendar not found: '%s'.", calendarId);
continue;
}

// Find events
const events = Calendar.Events.list(calendarId, {
const source_events = Calendar.Events.list(calendarId, {
timeMin: startTime.toISOString(),
timeMax: endTime.toISOString(),
singleEvents: true,
orderBy: 'startTime',
});

// If nothing find, move to next calendar
if (!(events.items && events.items.length > 0)) {
// If nothing is found, move to next calendar
if (!(source_events.items && source_events.items.length > 0)) {
continue;
}

events.items.forEach((event) => {
// Don't copy "free" events.
if (event.transparency && event.transparency === 'transparent') {
return;
// delete target event if its not in the source calendar anymore
target_events.forEach((target_event) => {
const source_event = source_events.items.filter((candidate_event) => (candidate_event.iCalUID == target_event.iCalUID))[0];
if (!source_event){
requestBody_delete.push({
method: 'DELETE',
endpoint: `${ENDPOINT_BASE}/${CALENDAR_TO_MERGE_INTO}/events/${target_event.id}`,
});
}

requestBody.push({
method: 'POST',
endpoint: `${ENDPOINT_BASE}/${CALENDAR_TO_MERGE_INTO}/events`,
requestBody: {
summary: `${SEARCH_CHARACTER}${calendarName} ${event.summary}`,
location: event.location,
description: event.description,
start: event.start,
end: event.end,
},
});
});
}

if (requestBody && requestBody.length) {
const result = new BatchRequest({
batchPath: 'batch/calendar/v3',
requests: requestBody,
submitRequest(requestBody_delete, "delete")


// check if target event exists and update it if it does. if not, then create it.
requestBody_update = []
requestBody_create = []
let new_description = ""

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this.


source_events.items.forEach((source_event) => {
// Only consider events that are not "free".
if (source_event.transparency !== 'transparent') {

const target_event = target_events.filter((candidate_event) => (candidate_event.iCalUID == source_event.iCalUID))[0];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be candidate_event.iCalUID === source_event.iCalUID? What if candidate_event is null?


// if target event is found, update it. else create it.
if (target_event) {
requestBody_update.push({
method: 'PUT',
endpoint: `${ENDPOINT_BASE}/${CALENDAR_TO_MERGE_INTO}/events/${target_event.id}`,
requestBody: {
summary: `${SEARCH_CHARACTER}${source_event.summary} ${calendarName}`,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please move this prefix calendarName to the beginning like the main code does?

location: source_event.location,
description: source_event.description,
start: source_event.start,
end: source_event.end,
},
});
} else {
requestBody_create.push({
method: 'POST',
endpoint: `${ENDPOINT_BASE}/${CALENDAR_TO_MERGE_INTO}/events`,
requestBody: {
summary: `${SEARCH_CHARACTER}${source_event.summary} ${calendarName}`,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the PUT call create a calendar with name "Test [Work]", will the POST for update it to "Test [Work] [Work]"? Looks like a bug.

location: source_event.location,
description: source_event.description,
start: source_event.start,
end: source_event.end,
iCalUID: source_event.iCalUID,
},
});
}
}
});

if (result.length !== requestBody.length) {
console.log(result);
}

console.log(`${result.length} events created.`);
} else {
console.log('No events to create.');
submitRequest(requestBody_update, "update")
submitRequest(requestBody_create, "create")
}
}