❄️
【Snowflake】Snowflake Notebook内で作成したファイルやグラフ画像をそのままSlackへ「送信」する方法
本記事で参考になるケース
- Snowflake内からSlackにファイルや画像を送信したい
- 特にSnowflake Notebook上で作成したグラフ画像やテーブルをcsvにしたファイルをSlackへ送信したい
- GET_PRESINGED_URL等は利用しない (Slackに完全に送信する形式を取ります)
メッセージのみを送信する方法は下記記事に記載
記事の概要
- Slack通知までに必要なSnowflake内の設定を記載
- Slack側はIncoming Webhookを利用する方法とSlack Appを利用する方法がありますが、今回はSlack Appを利用する方法で記載
- Snowflake External Network Accessを利用
方法
1. Slackへのネットワークを設定
CREATE DATABASE SLACK;
CREATE SCHEMA SLACK.NOTIFY;
CREATE OR REPLACE NETWORK RULE SLACK.FILE_NOTIFY.RULE
MODE = EGRESS
TYPE = HOST_PORT
VALUE_LIST = ('slack.com', 'files.slack.com');
CREATE OR REPLACE SECRET SLACK.FILE_NOTIFY.SLACK_APP_TOKEN
TYPE = GENERIC_STRING
SECRET_STRING = 'xoxb-xxxxxxxx';
-- SECRET_STRINGに設定された xoxb-xxxxxx は
-- Slack Appの OAuth Tokens For Your Workspace ・ Bot User OAuth Token である
CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION SLACK_FILE_API
ALLOWED_NETWORK_RULES = ( SLACK.FILE_NOTIFY.RULE )
ALLOWED_AUTHENTICATION_SECRETS = ( SLACK.FILE_NOTIFY.SLACK_APP_TOKEN )
ENABLED = true;
2. Slackへ画像やファイルを投稿する際に利用する関数を作成
CREATE DATABASE USERDB;
CREATE SCHEMA USERDB.UTIL;
CREATE OR REPLACE FUNCTION USERDB.UTIL.SLACK_POST_FILES(
channel varchar , files array
)
RETURNS varchar
LANGUAGE python
RUNTIME_VERSION = 3.9
HANDLER = 'main'
EXTERNAL_ACCESS_INTEGRATIONS = (SLACK_FILE_API)
PACKAGES = ('requests')
SECRETS = ('cred' = SLACK.FILE_NOTIFY.SLACK_APP_TOKEN)
AS
$$
import _snowflake
import json
import os
import requests
import base64
def main(
channel,
files
):
api_key = _snowflake.get_generic_secret_string('cred')
id_list=[]
for i in range(0, len(files), 3):
filename=files[ i : i + 3][1]
data=base64.b64decode(files[ i : i + 3][2].encode('ascii'))
response_get = requests.get(
url='https://slack.com/api/files.getUploadURLExternal',
headers={'Authorization': 'Bearer '+ api_key},
params ={ 'filename' : str(filename), 'length' :len(data)}
)
if not response_get.json()['ok']:
return str(response_get.json())
id_list.append([ files[ i : i + 3][0],response_get.json()['file_id']])
response_post_file = requests.post(
url=response_get.json()['upload_url'],
data=data
)
if response_post_file.status_code != requests.codes.ok:
return str(response_post_file)
response_post_chennel = requests.post(
url='https://slack.com/api/files.completeUploadExternal',
headers={'Content-Type': 'application/json', 'Authorization': 'Bearer '+ api_key},
json = {
'files': [{'title': f[0], 'id': f[1]} for f in id_list],
'channel_id': channel
}
)
return str(response_post_chennel.json())
$$;
3. ユーザ関数実行権限を付与(ここは読み飛ばしてもOK)
GRANT USAGE ON DATABASE USERDB TO ROLE <user_role>;
GRANT USAGE ON SCHEMA USERDB.UTIL TO ROLE <user_role>;
GRANT USAGE ON USERDB.UTIL.SLACK_POST_FILES(VARCHAR, ARRAY) TO ROLE <user_role>;
4. slackへテーブルのcsvファイルやグラフ画像や通知
①Snowflake NotebookでSQLの結果をcsvファイルとしてSlackに投稿
Notebookの記載
Slack通知結果 (SLACK_POST_FILES引数の「投稿タイトル」を'test'としている)
コピペ用
import streamlit as st
import pandas as pd
import base64
from io import BytesIO
from snowflake.snowpark.context import get_active_session
session = get_active_session()
buf=BytesIO()
################################
# cell2 の結果をcsvとして読み込み
################################
df=cell2.to_pandas().to_csv(buf, index=False)
arg=base64.b64encode(buf.getvalue()).decode('ascii')
channelId = 'CXXXXXXX'
################################
# Slackにcsvファイルとして投稿
################################
sql = f"select select USERDB.UTIL.SLACK_POST_FILES('{channelId}',['投稿タイトル','ファイル名.csv','{arg}'])"
data = session.sql(sql).collect()
②Snowflake Notebookで生成したグラフ画像をSlackに投稿
Notebookの記載
Slack通知結果
コピペ用
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
from io import BytesIO
r = np.random.RandomState(42)
m = [0, 0]
c = [[1, 2], [2, 5]]
X = r.multivariate_normal(m, c, 100)
fig = plt.figure()
plt.scatter(X[:, 0], X[:, -1])
print(plt)
import base64
from io import BytesIO
buf = BytesIO()
plt.savefig(buf, format="png")
arg=base64.b64encode(buf.getvalue()).decode('ascii')
channelId = 'CXXXXXXX'
################################
# Slackに画像ファイルとして投稿
################################
sql = f"select select USERDB.UTIL.SLACK_POST_FILES('{channelId}',['投稿タイトル','ファイル名.png','{arg}'])"
data = session.sql(sql).collect()
発展(複数のファイルをまとめてSlackに1回で投稿する
USERDB.UTIL.SLACK_POST_FILES
の引数であるARRAYに追加
################################
# Slackに画像ファイルとして投稿
################################
sql = f"select select USERDB.UTIL.SLACK_POST_FILES(
'{channelId}',
[
'投稿タイトル1','ファイル名1.png','{arg1}',
'投稿タイトル2','ファイル名2.png','{arg2}',
...
.
]
)"
data = session.sql(sql).collect()
Slack通知結果 (SLACK_POST_FILES引数の「投稿タイトル」を'title1','title2'とし、ファイルをテキストにしている)
まとめ
Snowflake Notebookの定期実行機能と合わせると、定期的に自動で分析/グラフ作成&SlackにPOSTしてくれるようにできたりと、夢が広がりますね!
参考
Snowlfake データクラウドのユーザ会 SnowVillage のメンバーで運営しています。 Publication参加方法はこちらをご参照ください。 zenn.dev/dataheroes/articles/db5da0959b4bdd
Discussion