Generating a Timeline from Google Calendar

Recently, I wrote a script to produce a timeline of upcoming events that would help our Office Coordinator, Terri Vruggink, prepare and plan for guests visiting our building. The timeline is simple, but it provides more details about upcoming events than the standard Google Calendar print view.

My script queries the Google Calendar API to retrieve events from several different calendars (particularly our conference rooms), then lists the events in order by start time, with details about the organizer, attendees, location, and duration.

While this is all information that we could pull directly from the Google Calendar web interface, a printed timeline provides a more concrete reference which is easier to survey when determining where to direct a guest. The implementation details of the timeline generator are not particularly exciting, but the output is incredibly helpful for smoothing our front desk operations.

How to Generate the Timeline

The heavy lifting occurs in the calls to the Google Calendar API; iterating through all specified calendars, and all events for the day, storing pertinent event details as hashes in the events array.


events = []
calendar_ids.each do |calendar_id|
  calendar_name = service.get_calendar(calendar_id).summary

  # Returns only events for today
  response = service.list_events(calendar_id,
                                 single_events: true,
                                 order_by: 'startTime',
                                 time_min: Date.today.to_time.iso8601,
                                 time_max: (Date.today + 1).to_time.iso8601)

  response.items.each do |event|
    name = event.summary
    start_time = event.start.date_time || event.start.date
    stop_time = event.end.date_time || event.end.date

    # If the stop_time / start_time are not DateTime objects, this
    # represents an all-day event, which we do not wish to display
    # so we skip them
    next unless (stop_time.instance_of? DateTime) && \
                (start_time.instance_of? DateTime)
    
    # Human friendly formatting of duration
    duration = distance_of_time_in_words(stop_time.to_time -
                                         start_time.to_time)
                                         
    # Formats the start_time and stop_time in a very human 
    # friendly format: 1:00 PM; 10:00 AM, etc.
    display_start_time = start_time.strftime('%l:%M %p').strip
    display_stop_time = stop_time.strftime('%l:%M %p').strip

    attendees = []
    organizer = ''

    event&.attendees&.each do |attendee|
      # We do not want to list attendees that are room resources
      unless attendee.resource
        attendees << (attendee.display_name || attendee.email)
      end
      
      # Set organizer if an attendee is labeled as the organizer
      if attendee.organizer
        organizer = (attendee.display_name || attendee.email)
      end
    end

   
    events << { name: name,
                start_time: start_time,
                stop_time: stop_time,
                display_start_time: display_start_time,
                display_stop_time: display_stop_time,
                duration: duration,
                attendees: attendees,
                organizer: organizer,
                calendar: calendar_name }
  end

end

I sort all events by start time, filtering out duplicate events (with the same start time and name):


events = events.sort_by { |event| event[:start_time] }
events = events.uniq do |event|
  "#{event[:start_time]} #{event[:name]}"
end

The events array is rendered as HTML with an ERB template. The HTML template groups the events by hour of the day to ease navigating in a timely fashion:


  <h1>Timeline for <%= Date.today %></h1>
  <div class="timeblock">
    <% (8..18).each do |hour| %>
    <% if events.find{|event|event[:start_time].hour == hour} %>
    <h1 class="hour"><%= hour_as_time(hour) %></h1>
    <div class="events">
      <% events.find_all{|event|event[:start_time].hour == hour}.each do |event| %>
        <div class="event">
          <h2><%= event[:display_start_time] %> :: <%= event[:name] %></h2>
          <h3><%= event[:calendar] %></h3>
          <ul>
            <li>End Time: <%= event[:display_stop_time] %></li>
            <li>Duration: <%= event[:duration] %></li>
            <% unless event[:organizer].blank? %>
            <li>Organizer: <%= event[:organizer] %></li>
            <% end %>
          </ul>
          <% unless event[:attendees].empty? %>
            <h3>Attendees</h3>
            <ul>
            <% event[:attendees].each do |attendee| %>
              <li><%= attendee %></li>
            <% end %>
            </ul>
          <% end %>
        </div>
    <% end %>
    </div>
    <% end %>
    <% end %>
  </div>

Timeline Output

When run against a few calendars, the rendered HTML output of the script populated with sample events as follows:

atomic_google_calendar_printout

If you are interested, I have published the full source for my script along with instructions for how to customize it for specific Google Calendars.

Conversation
  • Justin, this is so cool. It’s funny, I’m desperately trying to find a good timeline tool and I am not liking the options out there so far. Any chance you can show me what this would look like when you’re looking at a longer duration (e.g. 4 months)?

  • Comments are closed.