Table of contents
Open Table of contents
Introduction
A fun little recent script that I worked on - a Google Apps Script to write my Google Calendar events to a Google Sheet.
Why Even Do This?
No particular reason, and this was just for experimenting with the Google Apps Script. However, put some thought into it and one would quickly realize the potential that integrating the GSuite applications with Apps Script possesses.
Imagine a Project Management scenario - how much time is spent in planning for meetings, attending them, transcribing notes and action items, sending out meeting notes and following up on them? There is a case to be made of how Apps Scripts can automate a good chunk of this by building custom workflows that integrate
- Google Slides for decks
- Google Meet for the meetings
- Google Docs for notes
- Google Sheets for action items
- GMail for emails
This fun little project provided a good initiation into Apps Scripts’s much bigger capabilities.
Here’s How It Works
This little script grabs events from my calendar and plops them right into a Google Sheet. It even checks to see if I already have events recorded and only pulls in new stuff — so I don’t end up with duplicate entries.
The Script
Link to the full script - Github. The script is modular, with functions that handle specific actions.
1. formatDate(date)
- Purpose: Formats a date into the “YYYY-MM-DD” format for consistent display in the sheet.
- How It Works:
- Extracts the year, month, and day from the provided
date
. - Pads the month and day with zeros if necessary to maintain a two-digit format.
- Returns the formatted string in
YYYY-MM-DD
.
- Extracts the year, month, and day from the provided
function formatDate(date) {
var year = date.getFullYear(); // Extract the full year (e.g., 2024)
var month = String(date.getMonth() + 1).padStart(2, '0'); // Month is zero-based, so add 1; pad with 0 if single digit
var day = String(date.getDate()).padStart(2, '0'); // Extract the day of the month; pad with 0 if single digit
return `${year}-${month}-${day}`; // Format as "YYYY-MM-DD"
}
This function ensures uniform date formatting for entries in the sheet.
2. calculateDurationInMinutes(startTime, endTime)
- Purpose: Calculates the duration of an event in minutes, given its start and end times.
- How It Works:
- Converts the
startTime
andendTime
to Date objects. - Finds the time difference in milliseconds and converts it to minutes.
- Converts the
// Function to calculate the duration in minutes given start and end times
function calculateDurationInMinutes(startTime, endTime) {
var start = new Date(startTime); // Convert start time to Date object
var end = new Date(endTime); // Convert end time to Date object
var differenceInMs = end - start; // Calculate the difference in milliseconds
return differenceInMs / (1000 * 60); // Convert milliseconds to minutes
}
This function helps track the duration of each event for better time analysis.
3. getStartDate(sheet, defaultDate)
- Purpose: Determines the start date for retrieving new events based on the last row in the sheet. If there’s existing data, it starts from the day after the latest recorded event; if not, it defaults to a provided date.
- How It Works:
- Checks the last row in the
sheet
. - If the last row contains event data, it retrieves the date in the “Start Time” column (column 3) and sets the
startDate
to one day after. - If no data exists, it returns the provided
defaultDate
.
- Checks the last row in the
// Function to determine the start date for event retrieval based on the sheet's last row
function getStartDate(sheet, defaultDate) {
var lastRow = sheet.getLastRow(); // Get the last row with data in the sheet
if (lastRow > 1) { // Check if there is existing data (assumes headers in the first row)
var lastDate = sheet.getRange(lastRow, 3).getValue(); // Get the latest "Start Time" from column 3 in the last row
var startDate = new Date(lastDate); // Convert last date to a Date object
startDate.setDate(startDate.getDate() + 1); // Set start date to one day after the last date
startDate.setHours(0, 0, 0, 0); // Set time to 12:00 AM (start of the day)
return startDate; // Return the calculated start date
} else {
return new Date(defaultDate); // If no data, use the specified default date
}
}
This function helps prevent duplicate entries by calculating a dynamic start date.
4. writeEventDetailsToSheet(sheet, row, eventDetails)
- Purpose: Writes details of a single event to a specified row in the Google Sheet.
- How It Works:
- Uses
sheet.getRange(row, column).setValue()
to place each attribute ofeventDetails
(e.g., title, start time, duration, etc.) in its corresponding column.
- Uses
// Function to write the details of a single event to the sheet at a specified row
function writeEventDetailsToSheet(sheet, row, eventDetails) {
// Set values in the sheet for each event detail in the specified columns
sheet.getRange(row, 1).setValue(eventDetails.dateOfEvent); // Column 1: Date of Event
sheet.getRange(row, 2).setValue(eventDetails.title); // Column 2: Event Title
sheet.getRange(row, 3).setValue(eventDetails.start_time); // Column 3: Start Time
sheet.getRange(row, 4).setValue(eventDetails.end_time); // Column 4: End Time
sheet.getRange(row, 5).setValue(eventDetails.duration); // Column 5: Duration in minutes
sheet.getRange(row, 6).setValue(eventDetails.colorOfEvent); // Column 6: Event Color
sheet.getRange(row, 7).setValue(eventDetails.description); // Column 7: Event Description
}
This function logs each event’s details in a consistent and organized manner.
5. getEvents()
- Purpose: The main function that retrieves events from Google Calendar and logs them into the Google Sheet.
- How It Works:
- Accesses the active Google Sheet and selects the “work_tracker” tab.
- Calls
getStartDate()
to determine the earliest date from which to pull events. - Sets the
endDate
as today at 11:59 PM to ensure all current day events are included. - Fetches events from the specified calendar between
startDate
andendDate
. - Iterates over each event, creating an
eventDetails
object containing attributes like title, start time, end time, location, color, duration, and formatted date. - Uses
writeEventDetailsToSheet()
to log each event’s details to the sheet, starting at the row after the last row with data. - Logs a message confirming successful addition.
// Main function to retrieve events from Google Calendar and log them into the spreadsheet
function getEvents() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Access the active spreadsheet
var sheet = ss.getSheetByName("sheet_name"); // Select the sheet by name "sheet_name"
// Determine the start date for retrieving events based on existing sheet data
var startDate = getStartDate(sheet, "November 10, 2024 00:00:00"); // Default start date if no data in sheet
// Set the end date to the current day at 11:59 PM for retrieving today's events
var endDate = new Date();
endDate.setHours(23, 59, 59, 999); // Set end time to the end of the day
var cal = CalendarApp.getCalendarById("[email protected]"); // Get Google Calendar by its ID
var events = cal.getEvents(startDate, endDate); // Fetch events within the specified date range
var lastRow = sheet.getLastRow(); // Get the last row in the sheet to start appending events
var firstUpdateRow = lastRow + 1; // Row where new event data will begin
// Loop through each event to extract details and log them in the sheet
events.forEach((event, i) => {
// Create an object to store details of the current event
var eventDetails = {
title: event.getTitle(), // Event title
start_time: event.getStartTime(), // Event start time
end_time: event.getEndTime(), // Event end time
location: event.getLocation(), // Event location
description: event.getDescription(), // Event description
colorOfEvent: event.getColor(), // Color ID of the event
duration: calculateDurationInMinutes(event.getStartTime(), event.getEndTime()), // Duration in minutes
dateOfEvent: formatDate(event.getStartTime()) // Formatted event date as "YYYY-MM-DD"
};
// Write the current event's details to the sheet in the appropriate row
writeEventDetailsToSheet(sheet, firstUpdateRow + i, eventDetails);
});
Logger.log("Events have been added to the Spreadsheet"); // Log completion message
}
This main function orchestrates the entire process of fetching and logging events, leveraging helper functions for formatting, duration calculation, start date selection, and sheet writing.
How to Use It
To deploy this code in a Google Sheet and have it automatically pull events from Google Calendar, follow these steps:
Step 1: Open Google Apps Script
- Open your Google Sheet where you want to log the calendar events.
- Go to Extensions > Apps Script. This opens the Google Apps Script editor.
Step 2: Add the Script Code
- In the Apps Script editor, delete any code in the script editor and paste the entire code from above.
- Make sure to replace
"[email protected]"
inCalendarApp.getCalendarById("[email protected]");
with your own Google Calendar ID (you can use your email if it’s the default calendar or the specific calendar ID if using a different one).
Step 3: Save the Project
- Click File > Save, name the project (e.g., “Calendar Event Logger”), and save it.
Step 4: Set Permissions
- Click Run > getEvents to test the script for the first time.
- You’ll be prompted to authorize access. Click Review Permissions, sign in with your Google account, and grant the necessary permissions.
- Since the script interacts with Google Calendar and Google Sheets, you’ll need to allow access to both.
- After authorizing, the script will run and start adding events to your “work_tracker” sheet (or the sheet name you specified).
Step 5: Add a Trigger to Automate the Script
To have this code run automatically on a schedule (e.g., daily), set up a trigger.
- In the Apps Script editor, click on the clock icon on the left sidebar (or go to Triggers > Manage triggers).
- Click + Add Trigger in the bottom right corner.
- For Choose which function to run, select
getEvents
. - For Select event source, choose Time-driven.
- Set up the frequency (e.g., daily, hourly) depending on how often you want the script to log events.
- Click Save.
Step 6: Test and Verify
- Go back to your Google Sheet to check if events are logged as expected in the “work_tracker” sheet.
- Manually run
getEvents
again if you want to check functionality before the trigger runs automatically.
Optional: Customizing Event Columns
To customize the columns or adjust other settings (e.g., colors), edit the writeEventDetailsToSheet
function in the code.
A Few Other Things
Permissions
The script requires access permissions to Google Calendar and Google Sheets. I believe in the practice of the principle of least privilege - an information security concept in which a user is given the minimum levels of access – or permissions – needed to perform his/her job functions.
While this is managed in the Script by limiting the Scripts access to only the Google Sheet that it is deployed for, it currently has complete access to the Calendar with edit access - ideally read only should be enough unless you want to also write back to the Calendar - there is way to do this via Calendar API settings.
Things To Try
While this is a good starting point, what would be exciting is if we could pull the project that the calendar event was for without having to explicitly mention that in the event name.
Color IDs could work - I am big fan of time blocking my calendar and the way I organize that is by given a custom color to each of my events, each color is mapped to a project label.
Unfortunately, the Google Calendar API does not provide for fetching the color labels, just the color IDs, and the background and foreground colors. A solution could be to maintain a dictionary of color ID and project label mapping in the script.
Go ahead, give the script a try and let me know what you think.
The Complete Script
/*** @OnlyCurrentDoc */
// Function to format a date as "YYYY-MM-DD" for consistent display
function formatDate(date) {
var year = date.getFullYear(); // Extract the full year (e.g., 2024)
var month = String(date.getMonth() + 1).padStart(2, '0'); // Month is zero-based, so add 1; pad with 0 if single digit
var day = String(date.getDate()).padStart(2, '0'); // Extract the day of the month; pad with 0 if single digit
return `${year}-${month}-${day}`; // Format as "YYYY-MM-DD"
}
// Function to calculate the duration in minutes given start and end times
function calculateDurationInMinutes(startTime, endTime) {
var start = new Date(startTime); // Convert start time to Date object
var end = new Date(endTime); // Convert end time to Date object
var differenceInMs = end - start; // Calculate the difference in milliseconds
return differenceInMs / (1000 * 60); // Convert milliseconds to minutes
}
// Function to determine the start date for event retrieval based on the sheet's last row
function getStartDate(sheet, defaultDate) {
var lastRow = sheet.getLastRow(); // Get the last row with data in the sheet
if (lastRow > 1) { // Check if there is existing data (assumes headers in the first row)
var lastDate = sheet.getRange(lastRow, 3).getValue(); // Get the latest "Start Time" from column 3 in the last row
var startDate = new Date(lastDate); // Convert last date to a Date object
startDate.setDate(startDate.getDate() + 1); // Set start date to one day after the last date
startDate.setHours(0, 0, 0, 0); // Set time to 12:00 AM (start of the day)
return startDate; // Return the calculated start date
} else {
return new Date(defaultDate); // If no data, use the specified default date
}
}
// Function to write the details of a single event to the sheet at a specified row
function writeEventDetailsToSheet(sheet, row, eventDetails) {
// Set values in the sheet for each event detail in the specified columns
sheet.getRange(row, 1).setValue(eventDetails.dateOfEvent); // Column 1: Date of Event
sheet.getRange(row, 2).setValue(eventDetails.title); // Column 2: Event Title
sheet.getRange(row, 3).setValue(eventDetails.start_time); // Column 3: Start Time
sheet.getRange(row, 4).setValue(eventDetails.end_time); // Column 4: End Time
sheet.getRange(row, 5).setValue(eventDetails.duration); // Column 5: Duration in minutes
sheet.getRange(row, 6).setValue(eventDetails.colorOfEvent); // Column 6: Event Color
sheet.getRange(row, 7).setValue(eventDetails.description); // Column 7: Event Description
}
// Main function to retrieve events from Google Calendar and log them into the spreadsheet
function getEvents() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Access the active spreadsheet
var sheet = ss.getSheetByName("google_sheet_name_placeholder"); // Select the sheet by name "work_tracker"
// Determine the start date for retrieving events based on existing sheet data
var startDate = getStartDate(sheet, "November 10, 2024 00:00:00"); // Default start date if no data in sheet
// Set the end date to the current day at 11:59 PM for retrieving today's events
var endDate = new Date();
endDate.setHours(23, 59, 59, 999); // Set end time to the end of the day
var cal = CalendarApp.getCalendarById("calendar_email_id_placeholder"); // Get Google Calendar by its ID
var events = cal.getEvents(startDate, endDate); // Fetch events within the specified date range
var lastRow = sheet.getLastRow(); // Get the last row in the sheet to start appending events
var firstUpdateRow = lastRow + 1; // Row where new event data will begin
// Loop through each event to extract details and log them in the sheet
events.forEach((event, i) => {
// Create an object to store details of the current event
var eventDetails = {
title: event.getTitle(), // Event title
start_time: event.getStartTime(), // Event start time
end_time: event.getEndTime(), // Event end time
location: event.getLocation(), // Event location
description: event.getDescription(), // Event description
colorOfEvent: event.getColor(), // Color ID of the event
duration: calculateDurationInMinutes(event.getStartTime(), event.getEndTime()), // Duration in minutes
dateOfEvent: formatDate(event.getStartTime()) // Formatted event date as "YYYY-MM-DD"
};
// Write the current event's details to the sheet in the appropriate row
writeEventDetailsToSheet(sheet, firstUpdateRow + i, eventDetails);
});
Logger.log("Events have been added to the Spreadsheet"); // Log completion message
}