Python による Microsoft Graph API を利用し、ユーザのOutlookスケジュール情報を取得してみました


## 概要
Microsoft Graph API を利用して Azure Active Directoryのあるグループに所属しているメンバーのOutlookスケジュールを期間指定して取得するための Python プログラムです。
手順は、以下となります。
    ①.Access Token の取得する
    ②.指定されたグループのメールアドレスをもとに、そのグループに所属している User-ID の取得する
    ③.その User-ID をキーとして、直近２日間の過去スケジュールを取得する。

なお、①の Access Token の取得については、[このプログラム](https://qiita.com/turupon/items/038b893712433ed23f0b) を使用しています。
また、②の User-ID の取得については、[このプログラム](https://qiita.com/turupon/items/583383a70ac1a700b912) を使用しています。

## 実行環境
macOS Big Sur 11.1 
python 3.8.3

## スケジュール取得対象グループ
[User-ID の取得プログラム](https://qiita.com/turupon/items/583383a70ac1a700b912) 内に定義されている、以下のグループ
    GROUP_ADDRESS_LIST = [
        'tech-gp1@hogehoge.local',
        'tech-gp2@hogehoge.local'
    ]
に所属しているユーザの直近２日間の過去スケジュールを取得してみます。


## 実行するプログラム

```python:GetUserEvent.py

#
# User-IDから、そのユーザの指定期間でのイベント情報を取得する
#
import json
import requests
import argparse
import time
import pprint
from datetime import datetime, timedelta, date
from collections import OrderedDict
from GetAzureAccessToken import get_azure_access_token  # Azureアクセスのためのアクセストークンの取得オリジナル関数
from GetAzureGroupMember import get_user_from_group     # GroupメンバーのUser-IDを取得するオリジナル関数


# Selectする期間の取得
def select_date() -> [str, str]:
    # 前日のデータを抽出するとき
    day0 = (date.today() - timedelta(days=2)).isoformat()  # ２日前
    day1 = date.today().isoformat()  # 本日

    return (day0, day1)


# 所属メンバーのスケジュール情報の取得
def get_groupid_userid(groupid, groupmember, access_token):

    # データ抽出期間の取得（前１週間）
    day = select_date()
    print(day[0] + " - " + day[1])

    # for文でループさせるために dict型へ変換
    json_groupid = json.loads(json.dumps(groupid))
    json_groupmember = json.loads(json.dumps(groupmember))

    # グループIDの取得
    for glist in json_groupid :
        gid = json_groupid[glist]
        # print(gid + " : " + glist)

        # グループメンバー各々のID取得
        for users in json_groupmember[glist] :
            username = users['displayName']
            userid = users['id']
            # print(userid + " : " + username)

            # 各メンバーのスケジュールを取得
            time.sleep(1)
            events = get_user_calendar(glist, username, userid, day, access_token)

            # 取得スケジュールをPOST
            if len(events) > 0 :
                post_user_calendar(glist, username, events)


# あるUserのスケジュールの取得
def get_user_calendar(glist, username, userid, day, access_token) -> list:

    # Microsoft Graphを実行するためのヘッダ情報
    headers = {
        'Authorization': 'Bearer %s' % access_token,
        'Prefer': 'outlook.timezone="Asia/Tokyo", outlook.body-content-type="html"'
    }

	# グループの id, ddisplayName, mail の取得のURL
    CalendarGet_URL = "https://graph.microsoft.com/v1.0/users/{" + userid + "}/calendarView?" + \
                        "startDateTime=" + day[0] + "&endDateTime=" + day[1] + \
                        "&$select=organizer,subject,start,end,location" + \
                        "&$orderby=start/dateTime"
    # print(CalendarGet_URL)

    # グループ情報取得のGetリクエスト
    res = []
    while True :
        res1 = requests.get(
            CalendarGet_URL,
            headers=headers
        )
        # requrest処理をクローズする
        res1.close

        # res1をjsonファイルに整形しスケジュール情報の取得
        try :
            res += res1.json()['value']
        except KeyError :
            print(res)
            return res

        # 11件以上のスケジュールが有る場合は再度取得する
        try :
            CalendarGet_URL = res1.json()['@odata.nextLink']
            print("--------------------  11 or More  ------------------------")
            # pprint.pprint(res)
            # print(CalendarGet_URL)
        except KeyError :
            print("-------------------- Less than 10 ------------------------")
            # pprint.pprint(res)
            break
    
    return res
    

# 取得したスケジュール情報を画面出力する
def post_user_calendar(glist, username, events):
    print("")
    print(glist + " : "  + username)

    for event in events :
        print("-----------------")
        print ("件名　　 : " + event['subject'])
        print ("場所　　 : " + event['location']['displayName'])
        print ("主催者　 : " + event['organizer']['emailAddress']['name'])
        print ("開始時刻 : " + (event['start']['dateTime']).replace('T', ' ').replace(':00.0000000', ''))
        print ("終了時刻 : " + (event['end']['dateTime']).replace('T', ' ').replace(':00.0000000', ''))
        print ("日時 : " + event['start']['dateTime'][0:10] + "  " + event['start']['dateTime'][11:16] + "-" + event['end']['dateTime'][11:16])


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='User-IDから、そのユーザの とある期間のスケジュール情報 を取得する')
    args = parser.parse_args()

    start = time.time()

    # Access Token の取得
    access_token = get_azure_access_token()

    # Group-ID と その所属メンバー情報の取得
    groupid, groupmember = get_user_from_group(access_token)

    # 所属メンバーのスケジュール情報の取得
    get_groupid_userid(groupid, groupmember, access_token)

    get_time = time.time() - start

    print("")
    print("取得時間:{0}".format(get_time) + " [sec]")
    print("")
```

## プログラムの実行
 
最初にヘルプを表示してみます。

```
$ python GetUserEvent.py -h
usage: GetUserEvent.py [-h]

User-IDから、そのユーザの指定期間でのイベント情報を取得する

optional arguments:
  -h, --help  show this help message and exit
```

では、ユーザのスケジュールを取得してみます。
プログラムの print文を参考に、以下の結果を確認ください。

```
$ python GetUserEvent.py
https://graph.microsoft.com/v1.0/groups?$filter=mail eq 'tech-gp1@hogehoge.local'&$select=id,displayName
[{'id': 'aaaaaaaa-5798-4bcc-9c29-bbbbbbbbbbbb', 'displayName': '04_技術本部/Group1'}]
https://graph.microsoft.com/v1.0/groups/{aaaaaaaa-5798-4bcc-9c29-bbbbbbbbbbbb}/members?$count=true&$select=id,displayName,mail
https://graph.microsoft.com/v1.0/groups?$filter=mail eq 'tech-gp2@hogehoge.local'&$select=id,displayName
[{'id': 'eeeeeeee-45e3-4985-add1-cccccccccccc', 'displayName': '04_技術本部/Group2'}]
https://graph.microsoft.com/v1.0/groups/{eeeeeeee-45e3-4985-add1-cccccccccccc}/members?$count=true&$select=id,displayName,mail
2021-03-01 - 2021-03-03
-------------------- Less than 10 ------------------------

tech-gp1@hogehoge.local : Satoh Koichi
-----------------
件名　　 : Citrix構築 打ち合わせ
場所　　 : WebEX
主催者　 : Yamada Taro
開始時刻 : 2021-03-01 15:00
終了時刻 : 2021-03-01 16:00
日時 : 2021-03-01  15:00-16:00
                         ：
                        中略
                         ：

--------------------  11 or More  ------------------------
-------------------- Less than 10 ------------------------

tech-gp2@hogehoge.local : Suzuki Ichiro
-----------------
件名　　 : 【至急】打合せ
場所　　 : Microsoft Teams 会議
主催者　 : Suzuki Ichiro
開始時刻 : 2021-03-01 08:00
終了時刻 : 2021-03-01 09:00
日時 : 2021-03-01  08:00-09:00
                         ：
                        中略
                         ：

取得時間:64.8772668838501 [sec]
```

２つのグループで総勢33名、登録スケジュール74件のデータを取得するのに 約65秒かかりました（もちろんプログラム実行環境に依存されます）。

## 注意事項
ユーザのスケジュールは、１回のHTTPリクエストにおいて最大10件までしか取得できません。11件以上のスケジュールを再度取得するロジックを追加しています。


## 参考情報
以下の情報を参考にさせていただきました。感謝申し上げます。
[Azure AD ユーザーアカウントの棚卸しに便利なスクリプト](https://qiita.com/tetsuya-ooooo/items/02e66dd35a102792402c)
[【自動化】PythonでOutlookの予定を抜き出す](https://qiita.com/konitech913/items/2ec831863ad84db23558)
[Microsoft Graph を使ってみよう : Graph エクスプローラー](https://qiita.com/kenakamu/items/17905fe66345b8e3754b)