昔作ったWebサービスをStreamlitで作り直した話
こんにちは 👋
今回は昔作った「文字列ジェネレーター」という Web サービスを Streamlit で作り直してみた話をしたいと思います。
新しく作ったもの
名前を「文字列ジェネレーター」から「Eastest」に変更しました。「easy」+「test」=「Eastest」という安直な名前です。
昔作った Web サービスの概要
昔作ったものについては上の記事に書いてあるのですが、簡単に説明すると
- 実務のテスト工程で、入力欄のバリデーションチェック用のテキストを作るのが大変だと感じていた
- 任意の文字または文字列を使用して任意の桁数の文字列を生成するサービスを開発した
という感じになります。
「バリデーションチェックのテストを自動化しましょうよ」という意見もあるかと思いますが、泥臭いやり方しか選択肢にない世界もあるので、今回も「そんな世界での悪あがき手段」として昔作ったサービスを改良していきたいと思います ✌️
「文字列ジェネレーター」の問題点
「バリデーションチェック用のテキスト生成を楽にする」という一番の目的は達成できていたものの、文字列ジェネレーターには以下の欠点がありました。
- ページから離れたりリロードすると生成されたテキストが消えてしまう
...はい、致命的ですねこれは 🤣
欠点改善の方針
前述した欠点の改善案として以下の 2 つの案を考えました。
- 生成したテキストを DB で管理する。
- 生成したテキストを JSON ファイルにエクスポートし、そのファイルをインポートすることでデータを復元する。
結論から言うと、2 の案を採用したのですが、なぜ 1 の案ではダメなのかと言うと、生成したテキストを DB で管理するということは、どのユーザーが生成したテキストなのか判別するためのユーザー ID のような情報が必要です。そしてこれはユーザー登録が必要であるということを意味しています。
ユーザー登録の何が問題なのかと言うと、「ユーザー登録というステップが入ることでサービス利用のためのハードルが上がってしまう」ということです。ユーザー登録という面倒な作業の対価がテキスト生成という機能だけというのはイマイチです。さらに、今回のサービスの利用シーンにはシステム開発のテスト工程というビジネス上のシチュエーションを考えているので、「Web サイトに何かを登録する」ということはできるだけ避けたいと考えたので、1 は不採用にしました。
一方で 2 の案ですが、ユーザー登録が不要かつ、JSON 形式でエクスポートできれば開発チームのメンバーにファイルを共有してサイト上で共有されたファイルをインポートすれば簡単にバリデーションチェック用のテキストをコピペできる環境をセットアップすることができるので、2 を採用しました。
使用技術
文字列ジェネレーターでは React を使っていましたが、今回は Streamlit を採用しました。
採用理由は以下の通りです。
- 実務の影響で Python 熱が上がっている
- UI を含めて Python コードで完結できる
- Streamlit Cloud から無料でデプロイできる
個人開発はモチベーションが命なので、好きな技術や気になる技術を使って楽しく開発ができるようにしましょう 💪😎💪
追加された機能の紹介
主な機能は文字列ジェネレーターの開発記事を参考にしてもらうとして、ここでは今回追加した機能を紹介したいと思います。
- テキスト生成方法切り替えタブ
テキスト生成方法として、ユーザーから入力された情報をもとに生成する方法と、前回エクスポートした JSON ファイルをもとに生成する 2 種類の方法があり、それらをタブで切り替えられるようにしました。
- JSON ファイルアップロードエリア
JSON ファイルのアップロードができます。
- JSON ファイルのエクスポートボタン
生成されたテキストたちを JSON ファイルとしてエクスポートできます。ファイルのエクスポートさえできればページから離れて生成されたテキストが消えたとしても ② からアップロードすることで前回生成されたテキストが復元されます。
- 生成されたテキストへのラベル付与
生成されたテキストが「何のために作られたのか」ということをメモ的に活用することができます。例えば、「住所入力欄のチェック用」、「半角英数字のチェック用」といった感じで活用してもらえたらと思っています。
実装で苦労した点
今回の実装で苦労したのは「生成されたテキストのコピー方法」です。
生成されたテキストはバリデーションチェックのテストに使用されることを想定しているので、ワンクリックでコピーしてテストしたい入力エリアにペーストできることが理想です。自分も経験しましたが、このコピペの繰り返しはテスト用のテキストを作成することより面倒です。したがって、この機能を削ることはなんとしても避けなければなりませんでした。
というわけで、Python でクリップボードへのコピー機能を使う方法を調べたところ、以下の方法があるようでした。
- pyperclip を使う
- tkinter をつかう
- OS 依存のコマンドを subprocess で実行する
どの方法も実装難易度にそれほど差がなかったので、情報が一番多く王道な pyperclip を使用して問題なく開発を進めていましたが、Streamlit Cloud へのデプロイ後にエラーが発生してコピー機能が使えなかったのです。
それもそのはず、前述の 1〜3 の方法はどれも結局のところ OS 依存のコマンドを実行するタイプのもののようでした。つまり、クラウドサーバー上の OS に対してコピーコマンドを実行しているため、クライアント側の PC 上のクリップボードにテキストはコピーされません。
つまり、JavaScript を使ってクライアント側でクリップボードへのコピーを行う必要がありました。しかし、「無数に生成されたうちの任意のテキストをコピーする」という JavaScript の処理を Streamlit でうまく扱う方法が見つからず、「生成されたテキストのテキストエリアにフォーカス → 全選択 → コピー で妥協かな? でもその方法だと、テキストエリアを disabled にできないからユーザーが誤って生成されたテキストを編集してしまうかのうせいがあるな」なんて脳内会議をしながら、何か参考になる部分がないかと Streamlit のデモサイトを徘徊していたところ、Streamlit のコードブロックにデフォルトでコピーボタンが付いていることが分かりました。
どうやらそのコピーボタンは sphinx(Python 関連のドキュメントを生成できるやつ)の sphinx-copybutton を流用しているようで、sphinx-copybutton ではまさに JavaScript を使用してクリップボードへのコピー機能を使用しているようでした。
というわけで、これまで disabled 属性をつけたテキストエリアに表示していた生成テキストをコードブロックに表示することで無事にワンクリックでコピー機能を使うことができるようになりました 🎉
最後に
Python で何かしたいという思いから今回初めて Streamlit を使ってみましたが、フロントエンドの知識がなくても簡単にそれっぽい見た目のサイトを作ることができるので楽しかったです。ただ、細かい部分のレイアウトなんかは難しいので、「Web サイトを作る」というよりは、「データ分析や機械学習関連のワークスペースを作る」といった方面で使ったほうが良さそうです。わざわざ Python で Web サイトを作る理由を考えても。
あと、この記事を書いていて思ったのですが、現状だと JSON ファイルからのテキスト生成は、厳密に言うとテキストを「生成」しているのではなく、生成されたテキストを「復元」しているだけです。(エクスポートされた JSON ファイルの中身を見てもらえれば分かると思います。)
自分としては JSON ファイルは DB の代替として「生成したデータを保存しておくもの」と考えていたのですが、ユーザーが複数人で JSON ファイルを共有することを考えると、JSON ファイルを定義書的に使う、つまり、以下のようなイメージにした方が「バリデーションチェック用のテキストデータ」としての管理に繋がるのかなと考えています。
// 「abc123」という文字列をもとに10桁のテキストを生成した場合
// DB的に扱う現行版
[
{
"label": "半角英数字: 10桁",
"generated_text": "abc123abc1",
}
]
// 定義書的に扱う場合
// 読み込み時には'input_text'と'digits'をもとに再度テキストを生成する
[
{
"label": "半角英数字: 10桁",
"input_text": "abc123",
"digits": 10,
}
]
これを見ると定義書的に扱ったほうが良さそうに思いますが、バリデーションについては別途仕様書にまとめられているはずなので、わざわざこの JSON ファイルと二重管理にするのは大変とも思っています。
まぁ、このサイトをそんな本気で業務に組み込もうなんて思うことはないと思いますし、頑張ってバリデーションチェックのテストを自動化させた方がみんなハッピーになれるので、とりあえずは今のままにしておこうかなと考えています。
それでは、最後までお読みいただきありがとうございました。
Discussion