Lotus Notes 6.5のカレンダーをGoogle Calendarへ転送するRubyスクリプト
動機
Mozillaからスケジュール管理ソフトとして Sunbird Portable というのがあるのを知り、ToDo管理も含めて試そうと思い立ったものの、仕事ではLotus Notesのカレンダーなので、相互にやりとりできない。
SunbirdはGoogle Calendarとの同期ができるので、あとLotus NotesとGoogle Calendarとを同期させることができれば、Google Calendar経由でやり取りできる。(ちょっとまだるっこしいけれど)
で、Lotus NotesとGoole Calendarとの同期について検索していたところ、tokida氏ページを発見。
早速試してみたものの、Notesのバージョンの違い(当方はNotes 6.5)か上手くいかない。
そこからRubyの勉強しつつの試行錯誤が始まり、なんとか動かすことができた次第。
インストール
Rubyのインストールを含め次の手順で。RubyGemでインストールしたライブラリにはちょっと悩みました。
- Rubyのインストール
- http://www.garbagecollect.jp/ruby/mswin32/ja/ へアクセス
- Releaseからruby-1.8.7-p72-i386-mswin32.zip を取得
- 解凍したあと、binフォルダをPATHに入れる
- RubyGemのインストール
- http://rubyforge.org/frs/?group_id=126 へアクセス
- rubygems-1.3.1.zip を取得し、解凍
- 解凍したフォルダで ruby setup.rb を実行
- gcalapiのインストール
- http://rubyforge.org/frs/?group_id=2228&release_id=30319へアクセス
- gcalapi-0.1.2.gemを取得
- gem install gcalapi-0.1.2.gem としてインストール
- 後の方にあるバッチファイルを適当な名前で保存
gemの実行で zlib.dll がないと言われたときは、http://www.rubylife.jp/railsinstall/rubygems/index3.htmlを参考に準備。
設定
バッチファイルはRubyスクリプトになっているので、その中の定数定義を修正。
名称 | 設定内容 |
---|---|
NOTES_SERVER | Notesサーバ名 |
NOTES_DB | メールDB名 |
NOTES_PASSWORD | Notesパスワード。 nilにすると、実行時に入力となる |
GCAL_ACCOUNT | Google Calendarのアカウント(メールアドレス) |
GCAL_PASSWORD | Google Calendarのパスワード |
GCAL_FEED | Google CalendarのURL |
PROXY_ADDR | Google CalendarにアクセスするためのProxyサーバ名。nil だと直接アクセス |
PROXY_PORT | Proxy ポート |
DURATION_TO_SYNC | 転送範囲日数 |
FEEDのURLについて、XMLのリンクをコピーでよいですが、そのままでは上手くいかないようです。その場合、コピーしたURLのgoogle.com以下を、*google.com/private/full'というふうに /private/full とする上手くいきます。
実行
設定後のバッチファイルを実行する
転送範囲
予定、イベント、確認、記念日の4種類。
Notesの設計によっては上手くいかないものがあるかもしれませんが。
バッチファイル本体
@echo off ruby -x "%~f0" %* pause goto :EOF #! ruby -Ks require 'rubygems' # gcalapiをGemでインストールしたときに必要らしい require 'win32ole' require 'kconv' require 'googlecalendar/calendar' # 各種設定 NOTES_SERVER = 'server_name' # Notesサーバ名 NOTES_DB = 'database.nsf' # メールDB名 NOTES_PASSWORD = 'password' # Notesパスワード GCAL_ACCOUNT = 'mailadderss@gmail.com' GCAL_PASSWORD = 'password' GCAL_FEED = 'http://www.google.com/calendar/feeds/xxxx%40group.calendar.google.com/private/full' PROXY_ADDR = nil # 'proxy_server' PROXY_PORT = nil # 8080 DURATION_TO_SYNC = 30 # 今日から同期を取る範囲の日数 def get_range t = Time.now st = t.strftime("%Y/%m/%d 00:00:00") t = Time.mktime(t.year, t.month, t.day, 0, 0, 0) t += 60 * 60 * 24 * DURATION_TO_SYNC - 1 et = t.strftime("%Y/%m/%d %H:%M:%S") return st, et end def setup_notes ns = WIN32OLE.new('Lotus.NotesSession') if NOTES_PASSWORD ns.Initialize(NOTES_PASSWORD) else ns.Initialize end db = ns.GetDatabase(NOTES_SERVER, NOTES_DB) return db.GetView('Calendar') end def show_doc(doc) items = doc.items puts "----------" items.each { |item| print item.name, "=>" p item.values } end if PROXY_ADDR GoogleCalendar::Service.proxy_addr = PROXY_ADDR GoogleCalendar::Service.proxy_port = PROXY_PORT ? PROXY_PORT : 80 end gcal_server = GoogleCalendar::Service.new(GCAL_ACCOUNT, GCAL_PASSWORD) gcal_cal = GoogleCalendar::Calendar.new(gcal_server, GCAL_FEED) stime, etime = get_range() print stime, ' - ', etime, "\n" if $DEBUG begin view = setup_notes() rescue puts "Notesサーバへ接続できません" exit end # 今日から一定範囲のカレンダイベントを消去 st = Time.parse(stime) en = Time.parse(etime) begin gcal_cal.events( :'start-min' => st, :'start-max' => en, :'max-results' => 1000).each { |event| event.destroy! } rescue puts "Google Calendarへ接続できません" exit end keys = { } doc = view.GetFirstDocument n = 0 while doc atype = doc.GetItemValue('AppointmentType')[0] st = doc.GetItemValue('StartDateTime') et = doc.GetItemValue('EndDateTime') loc = doc.GetItemValue('Location')[0] subject = doc.GetItemValue('Subject')[0] #show_doc(doc) st.size.times { |i| printf("%s:%s-%s %s\t%s\n", atype, st[i], et[i], subject, loc) if $DEBUG if (stime <= st[i] && st[i] <= etime) || (stime <= et[i] && et[i] <= etime) key = st[i]+et[i]+subject if !keys.key?(key) keys[key] = 1 printf(">> %s:%s-%s %s\t%s\n", atype, st[i], et[i], subject, loc) if $DEBUG print st[i],"-",et[i], " ", subject, "\n" event = gcal_cal.create_event event.st = Time.parse(st[i]) event.en = Time.parse(et[i]) event.title = subject.toutf8 event.allday = (atype == "1" || atype == "2") ? true : false event.desc = ''.toutf8 event.where = loc.toutf8 event.save! n += 1 end end } doc = view.GetNextDocument(doc) end print '全部で ', n, "文書\n" __END__ :EOF
使用にあたって
本スクリプトはパスワードを直接埋め込んだりして使うようになっているので、管理には十分注意ください。
Notesを使用しているのはたぶん会社内だと思われますが、Googleへのアクセスには社内のルールなどがあると思うので、それに従うべきです。またカレンダーは個人情報であり、会議の情報などはいわゆるContidential情報になるので、使用者の責任において運用するようにしてください。
本スクリプトの運用の結果については一切関知しません。
謝辞
本スクリプトはtokida氏の情報がなければ作ることができませんでしたし、ベースはtokida氏のプログラムです。修正版の公開を快諾いただいたtokida氏に感謝します。