18
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

KLab EngineerAdvent Calendar 2018

Day 16

GoでGoogle Calendar APIをいじって会議室を探す話

Posted at

この記事は KLab Advent Calendar 2018 16日目の記事です。

はじめに

最近、業務でミーティングを開催する側になることが増えたのですが、日中は会議室が空いていないことが多く、特に参加メンバーが多い場合に予定のすり合わせで苦労することが増えました。

KLabでは会議室や備品などもGoogle Calendarのリソースとして登録されており、ブラウザ上から予約を行うのですが、
明日までに同僚10人が参加する1時間のミーティングを組まないといけない!
といった状況で、会議室が空いている時間と同僚10人のカレンダーをにらめっこするのはかなり骨が折れます。

そこで、予定のすり合わせをもっと簡単に行えないかと思い、最近社内でツール作成によく使われるGoの勉強がてら、Google Calendar APIをいじってみました。

GoでGoogle Calendar APIを叩く

Go Quickstart を見つつサンプル通りにやっていくだけで簡単に自分のカレンダーを取得できました。
(途中、Goのバージョンが古いと怒られたので1.9から1.11.2にあげました1

取得した予定を出力するとこのようになります。(伏せ字で見づらくてすみません)
goQuickstart.png

リソース(会議室)の予定を取得する

calendar-gen.go
func (r *EventsService) List(calendarId string) *EventsListCall {
	c := &EventsListCall{s: r.s, urlParams_: make(gensupport.URLParams)}
	c.calendarId = calendarId
	return c
}

google-api-go-client
リソース(会議室)の予定を取得するには、会議室のcalendarIdが必要です。
カレンダーIDはブラウザ上のGoogle Calendarから取得することができます。
まずは 同僚のカレンダーを追加 から必要なリソースを追加します。
colleague.png

追加したカレンダーを選択し、[設定]->[カレンダーの統合] と進むと
カレンダーIDが記載してあります。
calendarid.png
これで会議室の予定も取得できるようになりました。

予定の表現の仕方

自分と会議室の予定が取得できたら、それらから「双方の予定が空いている時間」を探します。
今回、上手いやり方が思いつかなかったので、ビット演算でやることにしました。

会議室の予約のほとんどは30分単位で行われているので、1日を30分ごとのブロックに分割し、予定がある = 1 予定がない = 0というビットで表現しました。

つまり、右から1ビット目が「0:00〜0:30」の予定を、2ビット目が「0:30〜1:00」の予定を、21ビット目が「10:00〜10:30」の予定を表しているという状態です。
1日は24時間なので、30分毎に分割しても48ブロックであり、64bitあれば余裕で表現することができます

例えば、12月16日の

  • 午前1時〜2時
  • 午前10時〜10時半
  • 午後15時〜16時半
  • 午後20時〜24時
    に予定が入っていた場合、
00 00 00 00 00 00 00 00              // 使用しない左16bit
11 11 11 11 00 00 00 01 11 00 00 00  // 左端は23:30、右端は12:00を表す
00 01 00 00 00 00 00 00 00 00 11 00  // 左端は11:30、右端は0:00を表す

つまり12月16日の予定を64ビット表記にすると
0000000000000000111111110000000111000000000100000000000000001100

と表すことができます。

自分と会議室の予定を比較

まず、Calendar APIから取得できる日付はただの文字列なので、扱いやすいようにtime.Time型にパースします

format := "2006-01-02T15:04:05-07:00"
startTime, err := time.Parse(format, item.Start.Datetime)

次に、日付とその日の予定を表すビット配列でmapを作り、
取得した予定のstartTime endTimeを元にビットを立てていきます。

calendar.go
m := map[string]uint64{}
for _, item := range events.Items {
    /*
      時刻のパースなど、省略
    */
    
    // 予定の開始時刻のビット位置を計算
    startTimeBit := uint64(startTime.Hour() * 2)
    if startTime.Minute() >= 30 {
        startTimeBit++
    }
    // 予定の終了時刻のビット位置を計算
    endTimeBit := uint64(endTime.Hour() * 2)
    if endTime.Minute() == 0 {
        endTimeBit--
    } else if endTime.Minute() > 30 {
        endTimeBit++
    }
    // 予定の時刻にビットを立てる
    date := item.Start.Date()  // 例: "2018-12-17"
    for i := startTimeBit; i <= endTimeBit; i++ {
        m[date] |= 1 << i
    }

実際に取得できた自分および会議室の予定のmapはこのようになります。2

sample
// 自分の予定
mySchedules := map[string]uint64{
    "2018-12-17": 0000000000000000111111110011100000100110110011111111111111111111,
    "2018-12-18": 0000000000000000111111111111101111111111110011111111111111111111,
}
// 会議室の予定
mtgRoomSchedules := map[string]uint64{
    "2018-12-17": 0000000000000000000000000011111111111111000000000000000000000000,
    "2018-12-18": 0000000000000000000000110101111111111011110000000000000000000000,
}

最後に、自分と会議室の予定を比較して両方が空いている時間を探します。
ビットが立っている位置が予定がある時刻なので、論理和を取って0となる位置(=どちらも予定がない時刻)を探します。

calendar.go
for key, _ := range mySchedules {
    // 論理和をとって自分と会議室両方が空いている時間を算出
    schedule := mySchedules[key] | mtgRoomSchedules
    // bitを時刻にデコード
    fmt.Printf("-------%v-------\n", key)
    for i := uint(0); i < 48; i++ {
        if schedule&(1<<i) != 1<<i {
            hour := i / 2
            minute := i % 2 * 30
            fmt.Printf("%v時 %v分 is OK!\n", hour, minute)
        }
    }
}

出力した結果がこちらです。

-------2018-12-17-------
19時 0分 is OK!
19時 30分 is OK!
-------2018-12-18-------
10時 0分 is OK!
10時 30分 is OK!

12月17日 19時〜20時 12月18日 10時〜11時 であれば
自分、会議室ともに空いていることがわかりました。

今後の展望など

今回は自分と会議室1つだけの予定を比較しましたが、他の会議室すべてと比較したり、同僚のカレンダーIDから予定を取得することで、冒頭の
明日までに同僚10人が参加する1時間のミーティングを組まないといけない!
といった状況でも、簡単に空いている時間を探すことができます。

また、今回は時間がありませんでしたが、予定の取得だけでなく登録もできるので、提示された選択肢を選ぶだけでカレンダー登録まで行えるようにしたいです。

ゆくゆくはSlackbotにすることで、誰もが簡単に(ブラウザでGoogle Calendarとにらめっこすることなく)ミーティングを設定できるようになればと思います。

  1. 1年に1回程しかgoを触っていないのがバレます。

  2. 早朝や深夜に会議室を使うことはまず無いので、最初からこの時間のビットは省けば32bitでも十分足りることに途中で気付きました。

18
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?