Download to excel with Streamlit in Snowflake

Download to excel with Streamlit in Snowflake | Tableau-id Press -タブロイド-
DownloadExcel

はじめに

Streamlit in Snowflakeでexcelで開けるCSVにダウンロードしたい、と要望がありました。なかなか手こずったので、同じようなお困りごとをお抱えの方の一助になればと思い、記します。

ソースコード

なにはともあれソースコードです。

注)以下を実行してからプログラムを走らせてください。

CREATE OR REPLACE STAGE database.schema.TEMP_STAGE_DOWNLOAD

# Import python packages
import pandas as pd
import streamlit as st
from snowflake.snowpark.context import get_active_session

#---------------------------------------
def get_excel_download_url(df: pd.DataFrame, filename: str) -> str:
  from io import BytesIO

  db = session.get_current_database()
  schema = session.get_current_schema()
  temp_stage = "TEMP_STAGE_DOWNLOAD"
  full_temp_stage = f"{db}.{schema}.{temp_stage}"
  session.sql(f"CREATE OR REPLACE STAGE {full_temp_stage} ENCRYPTION = (TYPE = 'SNOWFLAKE_SSE')").collect()

  in_stream = BytesIO(str.encode(df.to_csv(), encoding='SJIS'))
  session.file.put_stream(in_stream, f'{full_temp_stage}/{filename}', auto_compress=False)

  res = session.sql(f"select get_presigned_url(@{full_temp_stage},'{filename}', 3600) as url").collect()
  url = res[0]["URL"]
  return f"[Download {filename} 📥]({url})"

#---------------------------------------
session = get_active_session()
st.title("📥 Download Dataframe as Excel(csv)")

st.write(
  "Try editing the dataframe below, and then click the button to download it as a excel(csv)"
)
df = pd.DataFrame(
  {
    "a": ['か', 'き', 'け'],
    "b": [4, 5, 3],
    "c": [7, 8, 9],
  }
)
if st.button("Get download link"):
  st.write("Generating url...")
  st.write(get_excel_download_url(df, "test.csv"))
  st.info("Right click and choose 'Open in new tab'")

下記頁を参考にさせていただきました。ありがとうございました。

https://github.com/Snowflake-Labs/sf-samples/tree/main/samples/streamlit-in-snowflake/download

https://community.snowflake.com/s/question/0D5Do000017uhhSKAQ/how-can-i-trigger-a-local-data-download-from-a-streamlit-app-hosted-on-the-snowsight-ui

奮闘記

ここからは私の奮闘記なので、ご興味のある方のみお読みください。

1度目の「いける!」

最初は snowpark_df.write.copy_into_location() を使いました。

パラメータのFILE FORMATにENCODING = SHIFTJIS; を指定して、いける!と思ったのですが

ENCODINGの指定はデータロードのときのみ指定できるようです。ダメでした。

2度目の「いける!」

社内から「csvkit」使えばいけるんじゃない?のアドバイスを頂きチャレンジ。
csvkit の UnicodeCSVWriter をimportしたところで怒られました。
ならば、と UnicodeCSVWriter をソースに直書き。いける!と思いました。

ステージの読み込みでエラーになりました…。

n度目の・・・

SnowflakeFile を使えばステージを読める!いける!
SnowflakeFile はstreamlitでは直接使えないので、一旦Snowsightで関数を準備して、streamlitから呼ぶようにしました。
・・・読めたけど、書けませんでした。
(この辺りからおかしくなる・・・)
♬~消せないタブが増えていく
セリフ「消したら二度と巡り合えない…そんな気がする」
マシンを落とせない夜~Ah~♪
このあとも/tmpに書いてみたり(これが出来た発見は嬉しい。また後日に報告しますね)いろいろあって
「昔は1行ずつバッファに読み込んでnバイトずつコード変換したよなぁ」と思いながらコードを書き、
n止めの「・・・もぅ、お願いします・・・」で、表示された 「 か・き・け」!

おわりに

最後まで読んでいただき、ありがとうございます。
今回は うまくいったので良かったです。粘ってもうまくいかない事の方が多いので。。。
他の方法もあるのでしょうが、今は力尽きて…うまい方法があったら、是非、教えて頂けると助かります。