Brice Stacey home

Exporting Voyager Circulation Calendars to Google Calendar

I recently setup Voyager's Circulation Calendar for the next 6 months. That spans Spring Break, extended hours, and the Summer resulting in 118 exceptions. That's a lot to check for accuracy. I could probably have eliminated some of the redundancy in the exceptions by creating multiple calendars with sane defaults, but that'd probably add more complexity than what it's worth. So, I created a script to export a Voyager Circulation Calendar into Google Calendar so that you can better visual the results. Bonus features include calendar sharing - so others can verify its accuracy without needing access to Circulation in the SysAdmin module. Even better, Google's iCal support allows me to pull it into Outlook so even a Luddite is just a click away from looking up the calendar.

Check out the source on Github, which includes a readme.

require 'googlecalendar'
require 'active_record'

CONFIG = {
  :start => {
    :year => 2011,
    :month => 3,
    :day => 15
  },
  :end => {
    :year => 2011,
    :month => 8,
    :day => 25
  },
  :database => {
    :database => '',
    :username => '',
    :password => ''
  },
  :gcal => {
    :email => '',
    :password => ''
  },
  :calendar_id => 22
}

class Calendar < ActiveRecord::Base
  set_table_name :calendar
  set_primary_key :calendar_id
  has_many :holidays
end

class Holiday < ActiveRecord::Base
  set_table_name 'exception_calendar'
  set_primary_key 'calendar_id' # Exception_calendar primary key is actually (:calendar_id, :exception_date)
  belongs_to :calendar
end

ActiveRecord::Base.establish_connection(
  :adapter  => "oracle_enhanced",
  :database => CONFIG[:database][:database],
  :username => CONFIG[:database][:username],
  :password => CONFIG[:database][:password]
)

hours = Calendar.find_by_sql("SELECT * FROM calendar WHERE calendar_id = #{CONFIG[:calendar_id]}")[0]


g = Googlecalendar::GData.new
g.login(CONFIG[:gcal][:email], CONFIG[:gcal][:password])

# Iterate over the date from start to end
(Date.new(CONFIG[:start][:year], CONFIG[:start][:month], CONFIG[:start][:day])..Date.new(CONFIG[:end][:year], CONFIG[:end][:month], CONFIG[:end][:day])).each do |current|
  # Default to setting the open, close, and opened according to the calendar's default settings
  case current.strftime('%A')
    when 'Sunday'
      open = hours.sunday_openhour
      close = hours.sunday_closehour
      opened = hours.sunday_open
    when 'Monday'
      open = hours.monday_openhour
      close = hours.monday_closehour
      opened = hours.monday_open
    when 'Tuesday'
      open = hours.tuesday_openhour
      close = hours.tuesday_closehour
      opened = hours.tuesday_open
    when 'Wednesday'
      open = hours.wednesday_openhour
      close = hours.wednesday_closehour
      opened = hours.wednesday_open
    when 'Thursday'
      open = hours.thursday_openhour
      close = hours.thursday_closehour
      opened = hours.thursday_open
    when 'Friday'
      open = hours.friday_openhour
      close = hours.friday_closehour
      opened = hours.friday_open
    when 'Saturday'
      open = hours.saturday_openhour
      close = hours.saturday_closehour
      opened = hours.saturday_open
  end

  # Check for an exception on this day.
  holiday = hours.holidays.find(:first, :conditions=> "exception_date = '#{current.strftime('%Y-%m-%d 00:00:00')}'")
  # If we have an exception, set open, close, and opened according to the exception
  unless holiday.nil?
    open = holiday.exception_openhour
    close = holiday.exception_closehour
    opened = holiday.exception_open
  end

  # Cast from types in database to String
  open = open.to_s
  close = close.to_s
  opened = opened.to_s

  if opened == 'Y' 
    # We're open
    open = ' ' + open if open.length == 3    # Pad 3 digit hours (e.g. "930") with whitespace (e.g. " 930")
    close = ' ' + close if close.length == 3 # Pad 3 digit hours (e.g. "930") with whitespace (e.g. " 930")

    # Assumes the library always opens in the morning.
    # e.g. "Open 2011-03-15  7:30am-9:30pm"
    s = "Open #{current.strftime('%Y-%m-%d')} #{open[0..1]}:#{open[2..3]}am-#{(close[0..1].to_i - 12).to_s}:#{close[2..3]}pm"
  else 
    # We're closed
    # e.g. "Closed 2011-03-15"
    s = "Closed #{current.strftime('%Y-%m-%d')}"
  end
  puts "Creating: #{s}"

  # Add to default google calendar
  #g.quick_add(s)

  # Sleep so as not to be blocked by Google
  #sleep(1)
end