Microsoft Teamsのプレゼンスから部下がサボってないか監視しよう!~ 完全自動化Pythonプログラム ~

【目次】Teamsプレゼンス可視化ツール

0. プロローグ 1. 永続的なアクセストークンの取得 2. プレゼンスの取得 3. エクセルで可視化
4. PowerAutomate Desktopで定期実行 5. Pythonで実装 6. 完全自動化Pythonプログラム  

このシリーズかなり人気コンテンツです。「Teams 監視」「Teams 監視 会社」みたいな検索ワードで来られる方が多いこと多いこと。皆さん、そんなにサボってますか。まあサボっちゃいますよね。テレワークのメリットは色々ありますが、最大のメリットはベットが近くにあることですかね。そして最大のデメリットはベットが近くにあることですかねえ。

Microsoft Teamsプレゼンス自動取得Pythonプログラム

前回の記事で書いたPythonプログラムは人の出入りがあるたびにコードを書き換えないといけませんでした。私の会社はブラック企業なので人の出入りが多い!そのたびにコードを書き換えるのは面倒くさい!ということで、メンバーの増減が発生してもコードを書き換える必要がない完全自動化Pythonプログラムにアップデートしました。

プログラム動作フロー

動作フローは以下の通り。

  1. 特定のTeamsチャンネルからメンバーリストを取得
  2. 取得したメンバーリスト全員のTeamsプレゼンスを一括で取得
  3. TeamsプレゼンスをCSVファイルに書き込み

CSV形式で出力するので、あとはBIツールで分析するもよし、Excelに取り込むもよし好きなように加工できます。

プログラム① CSVファイルのヘッダー行とIDリストの作成

#!/usr/bin/python
# coding: UTF-8

#実行時間を取得
import datetime
dt_now = datetime.datetime.now()
datenow = "{0:%Y/%m/%d}".format(dt_now)
timenow = "{0:%H:%M:%S}".format(dt_now)
weeknow = "{0:%a}".format(dt_now)

import requests
import json
import os

#各種ファイルを変数に代入
tokenfile = "./token.txt"
idsfile = "./ids.json"
csvfile = "./presence.csv"
resultfile1 = "./result1.json"

#アクセストークンの読み取り
token = open(tokenfile, "r" , encoding="utf-8-sig")
access_token = token.read().strip()
token.close

headers = {'Authorization': 'Bearer {}'.format(access_token)}

# メンバーリスト取得
url1 = "https://graph.microsoft.com/v1.0/groups/[groups id]/members"
result1 = requests.get(url1, headers=headers,)
jsondata1 = (result1.json())

user_surname = [user["surname"] for user in jsondata1["value"]]

##メンバーリストのCSVファイル作成
#時刻書き込み
csv = open(csvfile, "w")
csvdata = [
    f"{datenow}",",",
    f"{timenow}",",",
    f"{weeknow}",",",
    ]
csv.writelines(csvdata)

#user_principal_names書き込み
for row in [user_surname]:
    csv.write(",".join(map(str, row)) + "\n") 
csv.close()

##idリストのJSONファイル作成
#id取得
ids = [user["id"] for user in jsondata1["value"]]
str_ids = json.dumps(ids)

idsjson = open(idsfile, "w")
idslist = [
    "{ \"ids\": ",
    ]
idsjson.writelines(idslist)

#idリスト書き込み
for row in [str_ids]:
    idsjson.write(",".join(map(str, [row])))

idslist = [
    " }",
    ]
idsjson.writelines(idslist)
idsjson.close()

#出力結果をファイルに保存
result = open(resultfile1, "w")
json.dump(jsondata1, result)
result.close

 解説

#アクセストークンの読み取り https://note.nkmk.me/python-requests-web-api/
token = open(tokenfile, "r" , encoding="utf-8-sig")
access_token = token.read().strip()
token.close

headers = {'Authorization': 'Bearer {}'.format(access_token)}

アクセストークンはこの手順で取得したものをファイルに保存し、読み取ります。”utf-8-sig”はDOM付きUTF-8の場合指定が必要です。読み取ったトークンをベアラー認証として使用します。

# メンバーリスト取得
url1 = "https://graph.microsoft.com/v1.0/groups/[groups id]/members"
result1 = requests.get(url1, headers=headers,)
jsondata1 = (result1.json())
 
user_surname = [user["surname"] for user in jsondata1["value"]]

続いてメンバーリストを取得します。取得するMicrosoft Graph APIエンドポイントは以下です。

https://graph.microsoft.com/v1.0/groups/[groups id]/members

[groups id]にはプレゼンスを取得したいメンバーが所属しているTeamsグループのIDを指定します。

変数名 surname に姓が入っているのでfor文で人数分取り出します。

##メンバーリストのCSVファイル作成
#時刻書き込み
csv = open(csvfile, "w")
csvdata = [
    f"{datenow}",",",
    f"{timenow}",",",
    f"{weeknow}",",",
    ]
csv.writelines(csvdata)
 
#user_principal_names書き込み
for row in [user_surname]:
    csv.write(",".join(map(str, row)) + "\n") 
csv.close()

CSVファイルのヘッダー行を作成します。日時と取り出したsurnameをカンマ区切りで書き込みます。

##idリストのJSONファイル作成
#id取得
ids = [user["id"] for user in jsondata1["value"]]
str_ids = json.dumps(ids)
 
idsjson = open(idsfile, "w")
idslist = [
    "{ \"ids\": ",
    ]
idsjson.writelines(idslist)
 
#idリスト書き込み
for row in [str_ids]:
    idsjson.write(",".join(map(str, [row])))
 
idslist = [
    " }",
    ]
idsjson.writelines(idslist)
idsjson.close()

同様にメンバーリストからUUIDを取り出します。UUIDは変数名 id に入っているのでfor文で取り出しつつ、JSONの辞書型として保存します。

これで土台となるCSVファイルが出来上がります。

プログラム② プレゼンスの取得とCSVファイルへの書き込み

#!/usr/bin/python
# coding: UTF-8

#実行時間を取得
import datetime
dt_now = datetime.datetime.now()
datenow = "{0:%Y/%m/%d}".format(dt_now)
timenow = "{0:%H:%M:%S}".format(dt_now)
weeknow = "{0:%a}".format(dt_now)

import requests
import json
import os
import shutil

#各種ファイルを変数に代入
tokenfile = "./token.txt"
idsfile = "./ids.json"
csvfile = "./presence.csv"
resultfile2 = "./result2.json"

#アクセストークンの読み取り
token = open(tokenfile, "r" , encoding="utf-8-sig")
access_token = token.read().strip()
token.close

headers = {'Authorization': 'Bearer {}'.format(access_token)}

##############
# プレゼンス取得  #
##############

#idリストの読み取り
ids = open(idsfile, "r")
idslist = json.load(ids)
ids.close

#URLをセット
url1 = "https://graph.microsoft.com/v1.0/communications/getPresencesByUserId"

#requests.postを使ってアクセスし、jsondataに格納
result1 = requests.post(url1, headers=headers, json=idslist)
jsondata1 = (result1.json())

presences = [user["availability"] for user in jsondata1["value"]]

#CSVファイル書き込み
csv = open(csvfile, "a")
csvdata = [
    f"{datenow}",",",
    f"{timenow}",",",
    f"{weeknow}",","
    ]
csv.writelines(csvdata)

for row in [presences]:
    csv.write(",".join(map(str, row)))

csvdata = [
    "\n"
    ]
csv.writelines(csvdata)
csv.close()

#出力結果をファイルに保存
result = open(resultfile2, "w")
json.dump(jsondata1, result)
result.close

2つ目のプログラムは定期的に実行してプレゼンスを追記していくプログラムです。

#idリストの読み取り
ids = open(idsfile, "r")
idslist = json.load(ids)
ids.close

 プログラム①で作成したUUIDリストを読み込みます。

#URLをセット
url1 = "https://graph.microsoft.com/v1.0/communications/getPresencesByUserId"
 
#requests.postを使ってアクセスし、jsondataに格納
result1 = requests.post(url1, headers=headers, json=idslist)
jsondata1 = (result1.json())
 
presences = [user["availability"] for user in jsondata1["value"]]

プレゼンスを取得するMicrosoft Graph APIエンドポイントは以下です。

https://graph.microsoft.com/v1.0/communications/getPresencesByUserId

このURLにJSONの辞書型にしたUUIDリストをPOSTメソッドで入力することでメンバー全員のプレゼンスを一括取得します。

プレゼンスは変数名 availability に入っているのでfor文で人数分取り出します。

#CSVファイル書き込み
csv = open(csvfile, "a")
csvdata = [
    f"{datenow}",",",
    f"{timenow}",",",
    f"{weeknow}",","
    ]
csv.writelines(csvdata)
 
for row in [presences]:
    csv.write(",".join(map(str, row)))
 
csvdata = [
    "\n"
    ]
csv.writelines(csvdata)
csv.close()

最後に日時と取り出したプレゼンス一覧をカンマ区切りで書き込めばメンバーのプレゼンス一覧CSVファイルの出来上がりです。

CSVファイルなのでエクセルに取り込めばこのように表示できます。エクセルの条件書式はこの記事を参考にしてください。終わり。

HT-A9に最適なスピーカースタンド

SONY HT-A9に最適なスピーカースタンドは

Mounting Dreamの MD5402-2M5x30ネジ4本です。

内容物はこんな感じ。スピーカー用アセンブリパーツはQのスペーサーのみ使用します。組立自体はマニュアル通りなので難しくありません。

スピーカーとL字のアタッチメントはこのようにスピーカーを載せて仮決めしてからネジ止めするとピッタリはまります。

ポールの内径は約28mmです。電源プラグがギリギリ通る幅なので延長コードを使用する際は大きさに注意が必要です。

スタンドを一番低くした状態。付属の電源ケーブルは20cmほどしか出ません。

こんな感じでセッティングできます。

付属の蝶ナットとスピーカーの壁掛け穴で固定するのはダメですか?

ダメです。

壁掛け穴はあくまでもスピーカーの自重で固定するためのものです。スタンドに置きつつ蝶ナットの組み合わせだとしっかり固定できません。

またL字のアタッチメントと背面と密着する格好となるため、クリアランスが無く電源ケーブルを通す場所がありません。

どこで買うか

 

公式サイトでは$79.99なのに日本のアマゾンでは約23000円と割高です。

そこで私がお勧めするのはAmazon.comでの購入です。日本への発送もでき約半額で購入できます。私は2セット購入で送料+関税で合計$244でした。

M5 x 30mmネジはこちら。43本も要らない人は近くのホームセンターで4本購入してください。

【完全無料】Microsoft Graph APIを個人アカウントで利用する方法

Microsoft Graph APIは企業利用のイメージが強いですが、無料で作成した個人アカウントでも利用できます。

Microsoft Graph により到達可能なユーザー

ユーザーの同意を得た上で、Microsoft Graph を使用することにより、ユーザーのプロファイル、OneDrive や Outlook のメール、予定表、連絡先などの Office サービス、および Windows のデバイスおよびアクティビティにアクセスすることができます。

更にMicrosoft Graph API自体は無料で利用できます。

Microsoft Graph の従量制課金 API とサービスの概要

ほとんどの Microsoft Graph API は標準 API です。 

・・・

定義済みの使用しきい値内の標準 API へのアクセスは、追加コストなしでユーザー ライセンスの一部として利用できます。

Microsoftの機械翻訳はちょっと何言ってるか分かりませんが、要はAPIコール自体は無料(追加費用なし)で、API経由で操作する側のサービスが有料であれば、お金がかかるよということです。すなわちExcel Online、Word Online、One Driveなどの無料で利用できるマイクロソフト製品は無料でMicrosoft Graph APIも利用できるということです。

Microsoft Graph APIは非常に便利なツールで且つ、無料とくれば使わない手はありません。そこで基本的な使い方をサンプルプログラムを通じて学んでいきたいと思います。サンプルプログラムはPythonを使ってExcel Onlineにデータを追記していく簡単なプログラムです。ステップは

  1. アクセストークンの取得
  2. Excel Online上の特定ファイルのファイルサイズを取得
  3. Excel Online上の特定ファイルにファイルサイズを追記

という流れです。無意味なプログラムですが、実行するたびにデータが変わり動作を理解するには十分だと思います。

Excel Online上の特定ファイルのファイルサイズを取得して追記するプログラム

#!/usr/bin/python
# coding: UTF-8

import requests
import json
import os
 
#tokenファイルを読み込み
tokenfile = "./data/token.txt"

#アクセストークンの読み取り
token = open(tokenfile, "r")
access_token = token.read().strip()
token.close
 
headers = {'Authorization': 'Bearer {}'.format(access_token)}

#Excel file sizeを取得

excelurl = "https://graph.microsoft.com/v1.0/me/drive/root:/GraphAPI/filesize.xlsx"

resultexcelfile = requests.get(excelurl, headers=headers)
excelfilejson = resultexcelfile.json()
excelfilesize = (excelfilejson['size'])
excelfilesizekilo = round(excelfilesize / 1024, 2)

#Microsoft Graph APIを使ってExcel Onlineを更新

# POSTするJSONデータをdict型で作成
dict = {
    "values" : [
        [
            f"{excelfilesizekilo}",
            ]
        ]
    }

#URLをセット
url = "https://graph.microsoft.com/v1.0/me/drive/items/9XXXXXXXXXXXXXXXXXXX0/workbook/tables/{DXXXXXX-AAAA-BBBB-CCCC-CDDDDDDDDDDC}/rows"

result = requests.post(url, headers=headers, json=dict)
jsondata = (result.json())

プログラム解説

#tokenファイルを読み込み
tokenfile = "./data/token.txt"

#アクセストークンの読み取り
token = open(tokenfile, "r")
access_token = token.read().strip()
token.close
 
headers = {'Authorization': 'Bearer {}'.format(access_token)}

まずはEntra IDのアクセストークンを取得します。取得方法はこちらの手順で取得したものをファイルに保存し利用します。読み取ったトークンをベアラー認証として使用します。

#Excel file sizeを取得

excelurl = "https://graph.microsoft.com/v1.0/me/drive/root:/GraphAPI/filesize.xlsx"

resultexcelfile = requests.get(excelurl, headers=headers)
excelfilejson = resultexcelfile.json()
excelfilesize = (excelfilejson['size'])
excelfilesizekilo = round(excelfilesize / 1024, 2)

Excel Onine上のファイル情報を取得するMicrosoft Graph APIエンドポイントは以下です。

GET https://graph.microsoft.com/v1.0/me/drive/root:/Path/To/filesize.xlsx

One Driveのルートディレクトリからファイルまでをフルパスで指定すればよいだけですが、

/drive/root:

という特殊な文字列をファイルパスの途中に入れる必要があります。

エクセルファイルのURLが指定できたらrequests.getを使ってファイル情報のJSONデータを取得し、’size’を指定してファイルサイズを取り出します。

最後に1024で割ってキロバイトにしておきます。

#Microsoft Graph APIを使ってExcel Onlineを更新

# POSTするJSONデータをdict型で作成
dict = {
    "values" : [
        [
            f"{excelfilesizekilo}",
            ]
        ]
    }

取得したファイルサイズをMicrosoft Graph APIを使ってExcel Online上のエクセルファイルに追記します。はじめに書き込むデータのdict型を作成します。この時、書き込むエクセルファイルのテーブルのカラムとデータの数が合っていないと書き込みできないので注意が必要です。

#URLをセット
url = "https://graph.microsoft.com/v1.0/me/drive/items/9XXXXXXXXXXXXXXXXXXX0/workbook/tables/{DXXXXXX-AAAA-BBBB-CCCC-CDDDDDDDDDDC}/rows"
 
result = requests.post(url, headers=headers, json=dict)
jsondata = (result.json())

Excel Onlineを更新するMicrosoft Graph APIエンドポイントは以下です。

POST https://graph.microsoft.com/v1.0/me/drive/items/[file id]/workbook/tables/[table id]/rows
Content-Type: application/json

{
    "values" : [
        [
            DATA1,
            DATA2,
        ]
    ]
}

[file id]、[tablel id]は実際のIDを入力します。

file id、table idはこちらの手順を参考にMicrosoft Graph Explorerを使って取得してください。

最後にrequests.postを使ってデータをPOSTすれば書き込み完了です。

以上。