HOW TO TD(User Engagement)Treasure Data User Engagement

Teams Webhookを利用したデータの定期チェック

ホーム » Teams Webhookを利用したデータの定期チェック

はじめに

カスタマーコンサルティングチームの谷口 翔です。

今回はTeamsのWebhookを用いてTreasure Data CDPに蓄積されたデータを可視化、Teamsに投稿することで定期的なチェックを行う体制を構築していきたいと思います。
少し古いデータですが、Teamsはグローバルではエンタープライズ企業でのシェアが65%(2019年9月)に達しています。

この記事を読まれている方のお勤め先でも導入されているところが多いのではないでしょうか。

今回は以下のステップで進めていきます。

  1. TeamsでWebhookの登録を行う
  2. 環境変数への登録を行う
  3. 投稿コードを記述する

対象者

  • Treasure Data CDPでデータの蓄積がある程度完了している
  • クエリの実行結果を都度ダウンロードし、Excelやスプレッドシートでチャートや集計表を作成していて工数がかかっている
  • BIツールの導入・運用はコスト面で時期尚早だと感じている
  • 社内コミュニケーションにTeamsを導入している

Incoming Webhookとは

外部のアプリケーションからTeams内にメッセージを投稿する際に使用する機能です。
Slack SDKと同様にPythonから扱いやすくするpymsteamsも提供されていますが、メンテナンスがあまりされておらずWebhookを直接使った場合に対してできることが制限されているため今回は使用しません。
また後ほど記載をしますが画像投稿には一部制限があるため注意してください。

詳細はリファレンスサイトを参照してください。

環境変数とは

環境変数とは、シェルから起動されたすべてのプロセスに有効な変数のことです。
これだとなんのことかイメージが湧きづらいので今回の記事と関係する点を挙げると、Treasure Data CDPに外部から接続するためのAPI KeyやSlackに接続するためのTokenなどが関係してきます。
上記のような漏洩することが大きな問題になるキー等について、プログラムに直接記述するのではなく環境変数に保存し必要に応じて呼び出すことでセキュリティを高めることができます。
今回の実行環境はMac OSのため、Terminalから~/.zshrcにSlack Tokenを書き込んでいます。

実行環境について

  • Mac OS 11.5.2
  • Python 3.9
  • pytd 1.4.0
  • pandas 1.3.4
  • matplotlib 3.4.3
  • seaborn 0.11.2
  • slack-sdk 3.13.0

使用するデータについて

使用するデータについては前回の記事と同じものを使用しています。

Incoming Webhookの登録

環境により表示される文言が異なることがあるので注意してください。

  1. Teamsを起動しアプリの検索から『webhook』を検索し選択します
    Teamsを起動しアプリの検索から『webhook』を検索し選択
  2. 『チームに追加』を選択する
    『チームに追加』を選択する
  3. 『チームまたはチャネルの名前を入力してください』とあるテキストボックスに登録したいチャネルの名前を入力します(例:daily-opportunity等)
    『チームまたはチャネルの名前を入力してください』とあるテキストボックスに登録したいチャネルの名前を入力
  4. 入力完了後『コネクタを設定』を選択します
    入力完了後『コネクタを設定』を選択
  5. チャネルのアイコンを変更する場合はイメージをアップロードしてください。そうでない場合はそのまま作成を選択してください。
    チャネルのアイコンを変更する場合はイメージをアップロード
  6. 作成完了すると、URLが発行されるのでそちらを使用します。外部に漏れないように保管してください。

環境変数への書き込み

先ほどのURLを環境変数に書き込みます。今回はTEAMS_WEBHOOKで書き込みました。
環境変数への書き込みについては前回の記事を参照してください。

Base64への変換

Webhookを使用する場合直接画像を投稿することができません。そのため、Base64という形式に画像を変換してテキストで送付を行う必要があります。

画像の圧縮

Teamsで15KB以上の画像をWebhookで投稿すると表示されないため、ファイルの圧縮が必要になります。主に解像度と品質を落として圧縮をおこないますが、圧縮しすぎると視認性が落ちるため場合によってはOneDriveにアップロードするなどの代替手段も検討してください。今回は[Python]画像ファイルを指定サイズ以下まで縮小するの記事の方法を拝借して圧縮をおこないます。

画像の圧縮

関数resize_img_file()内にある14000がファイルサイズを示しています(ここではファイルサイズが14KB以上の場合圧縮をするように指定しています)。
また、240については解像度の上限を示しており、解像度の幅が240px以上の場合縮小するように指定しています。
base64.b64encodeで圧縮した画像をBase64形式に変換し、requests.postで作成したWebhookのURLに投稿しています。

import pytd
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import os
from datetime import datetime
from PIL import Image, ImageFilter
import imghdr
import requests
import base64


def resize_img_file(file_path):
    is_resize = False

    # ファイルが画像ファイルかどうかを確認し、画像ファイルではない場合リサイズ処理は行わない
    img_type = imghdr.what(file_path)
    print(img_type)
    if img_type is None:
        return is_resize

    # ファイルサイズ確認
    if int(os.path.getsize(file_path)) >= 14000:
        is_resize = True

        img = Image.open(file_path)
        width = 240
        height = img.height * (240 / img.width)
        resize = img.resize((int(width), int(height)))
        resize.save(file_path)

        # 画質のデフォルトは75
        quality = 75

        while True:
            if int(os.path.getsize(file_path)) < 14000:
                break
            img = Image.open(file_path)
            img.save(file_path, quality=quality, optimize=False)
            if quality >= 5:
                quality -= 10
            else:
                break

    return is_resize


# 指定フォルダ配下にある画像ファイルを処理
def recursive_resize_img_file(file_path):
    if os.path.isdir(file_path):
        files = os.listdir(file_path)
        for file in files:
            recursive_resize_img_file(os.path.join(file_path, file))
    else:
        resize_img_file(file_path)

if __name__ == '__main__':
    now = datetime.now().strftime("%Y%m%d%H%M%S")
    client = pytd.Client(apikey=os.environ['SAMPLE_API_KEY'], endpoint='https://api.treasuredata.com/', database='sample_datasets', default_engine='presto')
    res = client.query('WITH t1 AS ( SELECT symbol ,TD_TIME_FORMAT(time,\'yyyy\',\'EST\') as year ,MAX(time) as max FROM nasdaq WHERE symbol IN (\'MSFT\',\'ADBE\') GROUP BY 1,2 )  SELECT nasdaq.symbol ,TD_TIME_FORMAT(nasdaq.time,\'yyyy\',\'EST\') as year ,nasdaq.close FROM nasdaq INNER JOIN t1 ON nasdaq.symbol = t1.symbol AND nasdaq.time = t1.max ORDER BY 2,1 ')
    df = pd.DataFrame(**res)
    filepath = '/Users/kakeru.taniguchi/Pictures/' + now + '_chart.png'
    webhook = os.environ['TEAMS_WEBHOOK']

    sns.lineplot(x="year", y="close", hue="symbol", data=df)

    plt.xticks(rotation=90)

    plt.savefig(filepath)
    plt.show()

    recursive_resize_img_file(filepath)

    with open(filepath, "rb") as file:
        base64image = base64.b64encode(file.read()).decode("ascii")
        image = "![]" + "(" + f"data:image/png;base64,{base64image}" + ")"

    data = {
        "title": "<タイトル>",
        "text": image
    }

    headers = {"Content-type": "multipart/form-data"}

    requests.post(webhook, json=data, headers=headers)

上記の設定に誤りがなければ、以下の様にTeamsに画像が投稿されていると思います。
Teamsに画像が投稿されている

おわりに

普段から使用しているコミュニケーションツール(今回の場合はTeams)に自動で投稿されることで、作成者・閲覧者双方の負担を減らしつつ可視化結果を共有できることが確認できたかと思います。日々の変化を簡単に確認できることで、施策効果の確認や改善に繋げられるのではと思います。
ただし前述しましたがTeamsでは画像投稿に制限があるので、ExcelをOneDriveに格納してリンクを送るなど代替手段も検討ください。

この記事が少しでも参考になりましたら幸いです。

商標

Excelは、米国Microsoft Corporationの米国およびその他の国における登録商標または商標です。
Teamsは、米国Microsoft Corporationの米国およびその他の国における登録商標または商標です。
Slackは、Slack Technologies, Inc.の登録商標です。

参考書籍

小久保 奈都弥(2020)『データ分析者のためのPythonデータビジュアライゼーション入門 コードと連動してわかる可視化手法』翔泳社.

参考サイト

こはた. “[Python] 画像ファイルを指定サイズ以下まで縮小する”. こはた. 2020-12-27.

谷口翔

Customer Analyticsチーム

2007年WILLCOMに入社、法人部門で新規事業における分析向けテーブルの設計や法人営業効率化のためのSFA導入などに従事。 2011年よりソフトバンクテレコム(現ソフトバンク)に出向後、5年間上層部向けの実績・営業戦略レポーティング業務に従事。その後インサイドセールスのデジタルマーケティング強化プロジェクトに参画、基幹データ/SFA/MA/CDPとのデータ連携設計から施策結果の可視化等に従事する。 2021年9月よりTreasure Dataに参画。顧客がCDPに蓄積したデータ利活用の推進に従事。

得意領域 : データ前処理(SQL/VBA/Google Apps Script)、データ可視化

Back to top button