TECH PLAY

セーフィー株式会社

セーフィー株式会社 の技術ブログ

221

この記事は Safie Engineers' Blog! Advent Calendar 16日目の記事です。 はじめに Gradioとは Gradioの主な特徴 Hello World! Gradioのインストール コード実装 実行結果 認証機能 リアクティブインターフェース ブロック構造とイベントリスナー コンポーネントの種類 主要コンポーネント一覧 実装例 ソースコード 画面 デプロイと共有 その他ドキュメント 関連記事 まとめ はじめに セーフィー株式会社 開発本部 第3開発部 AIVisionグループで画像認識AIの開発エンジニアをしている土井 慎也です。 セーフィーには2023年1月に入社し、もうすぐ1年が経とうとしています。 今年を思い返せば、生成系AIを中心とした、AI界隈の発展がすごい1年でした。 毎日のようにいろんな技術が発表されて、使えそうなものはすぐにOSSに実装されていて、技術進歩の速度がものすごく速く感じました。 さて、今回はそんなAI界隈で広く使われているWEB UIツールの Gradio について紹介したいと思います。 Gradioとは Gradioは、機械学習モデルを簡単にデモするためのPythonライブラリです。Gradioを使用すると、モデルの入力と出力に対応するインターフェースを簡単に作成でき、モデルを試すためのWeb UIを少ないコード量で生成できます。これにより、AIモデルの挙動を迅速に確認したり、他の人と共有したりすることが可能になります。 Gradioの主な特徴 シンプルなAPI : 数行のコードでUIを構築可能 多様なコンポーネント : テキスト、画像、音声など様々な入出力形式に対応 リアクティブな設計 : 入力変更に応じたリアルタイム更新 認証機能 : ユーザー認証システムの簡単な実装 HuggingFace連携 : Spacesとの統合によるモデルの共有と展開 カスタマイズ性 : レイアウトやスタイルのカスタマイズ stable diffusionで有名なWEB UIの一つである、 stable-diffusion-webui もGradioを使用しています。 AI用のGithubともいえる HuggingFace との親和性も高く、HuggingFaceで公開されているモデルを簡単に試すことができたり、逆に自分のモデルを公開することもできます。 また、機械学習モデルに関係なくても、Gradioには色々なインターフェイスが用意されているので、簡易的なWeb UIが簡単に実装できます。 Gradioは日々アップデートで変化しているため、今回はあまり深くは解説せず、基本的な紹介といたします。 Hello World! python3.8以上が実行可能な環境を用意します。 Gradioのインストール pip install gradio コード実装 import gradio as gr def greet (name): return "Hello " + name + "!" # インターフェースの作成 # fn: 実行する関数 # inputs: 入力のコンポーネントの種類 # outputs: 出力のコンポーネントの種類 demo = gr.Interface(fn=greet, inputs= "text" , outputs= "text" ) # Web UIの起動 demo.launch() このように、わずか数行のコードで、簡単にWeb UIを作成することができます。 HTMLやCSS、JavaScriptなどを意識する必要はありません! 実行結果 https://gradio-hello-world.hf.space 認証機能 Gradioには認証機能が組み込まれています。 demo.launch(auth=auth_function) とすることで、認証機能を有効にすることができます。 認証機能のベースはFastAPIの OAuth2PasswordRequestForm を使用しているようです。 import gradio as gr def greet (name): return "Hello " + name + "!" # 認証機能 def auth (user_name, password): # 例: ユーザー名とパスワード(反転)が一致したら認証OK # 実際にはDBと連携して認証するなどの処理が必要 if user_name == password[::- 1 ]: return True else : return False demo = gr.Interface(fn=greet, inputs= "text" , outputs= "text" ) # 認証機能を有効にする demo.launch(auth=auth) ログイン画面イメージ リアクティブインターフェース gr.Interface で live = True を指定すると、入力値を変更するたびに、リアルタイムで出力が更新されます。これにより、ユーザーは即座にフィードバックを得ることができ、インタラクティブな体験が向上します。 import gradio as gr def calculator (num1, operation, num2): if operation == "add" : return num1 + num2 elif operation == "subtract" : return num1 - num2 elif operation == "multiply" : return num1 * num2 elif operation == "divide" : return num1 / num2 # リアクティブインターフェースの作成 demo = gr.Interface( calculator, [ "number" , # 数値入力 gr.Radio([ "add" , "subtract" , "multiply" , "divide" ]), # 演算子選択 "number" # 数値入力 ], "number" , # 出力は数値 live= True , # リアルタイム更新を有効化 ) demo.launch() https://gradio-calculator-live.hf.space ブロック構造とイベントリスナー Blocksを使用すると、より細かくレイアウトを指定することができます。Interfaceよりも柔軟なUIデザインが可能になり、複雑なアプリケーションの構築に適しています。 また、イベントリスナーを使用することで、ボタンをクリックしたときの処理を指定することができます。これによりユーザーインタラクションに応じた動的な振る舞いを実装できます。 Hello World!をBlocksを使用して書き換えると、以下のようになります。 import gradio as gr def greet (name): return "Hello " + name + "!" # Blocksでレイアウトを構築 with gr.Blocks() as demo: # 入力コンポーネント name = gr.Textbox(label= "Name" ) # 出力コンポーネント output = gr.Textbox(label= "Output Box" ) # ボタンコンポーネント greet_btn = gr.Button( "Greet" ) # イベントリスナー: ボタンクリック時にgreet関数を実行 greet_btn.click(fn=greet, inputs=name, outputs=output, api_name= "greet" ) demo.launch() https://gradio-hello-blocks.hf.space 詳しいBlocksとイベントリスナーについては公式ガイドをご参照ください: www.gradio.app 詳しいレイアウトの方法について www.gradio.app コンポーネントの種類 Gradioには多種多様なコンポーネントが用意されています。一般的なものだと、text, number, checkbox, radio, dropdown, file, button, slider などがあります。 また、AI開発に特化した画像や音声、動画、グラフ、チャットなどの様々なコンポーネントも用意されています。これらを組み合わせることで、様々なAIモデルに対応したUIを構築できます。 主要コンポーネント一覧 基本入力 : Textbox, Number, Slider, Checkbox, Radio, Dropdown メディア入力 : Image, Audio, Video, File 高度な入力 : JSON, DataFrame, ColorPicker 出力表示 : Label, Image, Audio, Video, Plot, Gallery レイアウト : Row, Column, Tabs, Accordion コンポーネントの詳細な種類は公式ドキュメントをご参照ください www.gradio.app また、Gradioのメジャーアップデートでカスタムコンポーネントも最近追加されたので、今後ユーザーによって多くのカスタムコンポーネントが実装されていき、より種類が豊富になっていくと思います。 カスタムコンポーネントの作成方法 www.gradio.app 実装例 最近発表され、話題になっている動画生成AI、 MagicAnimate のWebUIになります。Gradioを使って洗練されたインターフェースが実装されています。 ソースコード huggingface.co 画面 https://zcxu-eric-magicanimate.hf.space zcxu-eric-magicanimate.hf.space デプロイと共有 Gradioで作成したアプリケーションは、様々な方法でデプロイ・共有することができます: ローカル共有 : demo.launch(share=True) でトンネリングURLを生成 HuggingFace Spaces : 無料でホスティングできるプラットフォーム 自前サーバー : FastAPIなどと組み合わせて独自サーバーにデプロイ 特にHuggingFace Spacesとの連携は簡単で、GitHubリポジトリを接続するだけで自動デプロイが可能です。 その他ドキュメント 公式ガイドとドキュメントを参照すると、より詳しい情報が得られます: www.gradio.app www.gradio.app 関連記事 実際にアプリを作ってみた記事です。良ければご覧ください。 engineers.safie.link まとめ Gradioが用意しているコンポーネントで事足りる場合、それらを組み合わせることで、AIに関わらずPythonで作られたソフトウェアは簡単にWeb UIを作成することができます。 フロントエンドの知識がなくても、簡単かつ迅速にWeb UIを作成することができるので、Pythonで開発したものをすぐにWebアプリ化したいAIエンジニアなどにとっては、非常に便利なツールだと思います。 セーフィーでも、Gradioを使用して社内向けに気軽にAIを試せるデモ環境を迅速に用意し、PoCやその他検証などに活用できないかを現在検証しています。
アバター
この記事は Safie Advent Calendar 15日目の記事です。 はじめに こんにちは。開発本部モバイルグループの池田です。 私は普段、モバイルグループのマネジメントおよび Safie Viewer for Mobile の PdM としてお仕事をしていますが、今年はそれに加えて 国際化対応 の開発PMとしても活動してきました。 今年の初めに海外展開のための組織が立ち上がり、そこの社内募集に手を上げて、兼務でお手伝いすることとなったためです。セーフィーではこのように、社内でも新しい試みにチャレンジしやすい制度が整っています。 実は、2023年中にセーフィーはグローバル進出のためのビジネス立ち上げ準備と開発を進めており、Safie Viewer モバイルアプリは英語・ベトナム語・タイ語への対応をリリースしています。また時差対応もほぼ完了し、以前はいくつか問題のあった日本以外のタイムゾーンでも、正しく快適に使えるようにアップデートされています。 エキゾチックSafie!! 今回は、主にモバイルアプリの開発に着目して、セーフィーのグローバル対応について書いてみたいと思います。(Web フロントエンドやサーバー、デバイスについても近々きっと書いてくれる人がいると思います!) はじめに 国際化(Internationalization)と地域化(Localization) 言語について 翻訳について 翻訳について・2 翻訳課題:ベトナム語意外と横長問題 翻訳課題:テキストデザインとFigmaのローカライズ 時間について おわりに 国際化(Internationalization)と地域化(Localization) ソフトウェアを異なる地域や時間に対応するためには、(トートロジー的ですが、)それが「 対応できるようにする 」必要があります。 扱う言語や地域に汎用性を持たせて、たとえばテキストをソースコードから分離して異なる言語に翻訳しやすくすること、また日付や通貨のフォーマットを動的に変更できるようにすることなどが含まれ、この作業を「国際化」と呼びます。この仕組みはすでに OS やプラットフォームにも含まれている部分が多くあります。 そうして国際化に対応したソフトウェアを、今度は特定の言語・地域に適合させるプロセスを「地域化(ローカライズ)」と呼びます。テキストの翻訳や日付・通貨フォーマットの現地化などの作業がこれにあたります。 一口にローカライズと言っても、そこには多くの要素が含まれます。 言語と文字コード、日時や数値の書式、暦、時差とタイムゾーン、各国の通貨や税金、法規制(電波関連法規や個人情報保護関連法規)への対応などなど。あるいは文化的な背景から利用・推奨できない表現やシンボルがあるかもしれません。ヘルプやユーザーサポートをどうしていくかも重要になってきます。 本記事では、その中でも特に大きな割合を占める 多言語対応とタイムゾーン対応 について触れていきたいと思います。 (どうでもいいですが、 i18n (internationalization) や l10n (localization)という略語、知っててもなかなか使う機会がないので今年はたくさん使えて満足です。笑) 言語について 冒頭にも触れた通り、今年の前半にキックオフされたグローバル進出PJにより、Safie Viewer は 英語、ベトナム語、タイ語 に順次対応していくこととなりました。 モバイルアプリの開発環境である XCode や Android Studio には、すでに言語ローカライズのための仕組みが用意されています。 iOS の場合は、XCode のプロジェクトの設定で ”Localizations” から対応言語を追加して、生成された Localizable.strings ファイルにその言語でのテキスト定義を追加していけば基本的に OK です。これらを追加することで、OS やアプリごとの言語設定に応じて、UIに表示される言語が切り替わってくれます。簡単ですね。 XCode プロジェクトの Localization また、リソースファイルの型安全性やオートコンプリートを提供してくれる R.swift の利用によってローカライズ開発の生産性を上げてもいます。 ベトナム語の Localizable.strings ファイル さて、アプリ UI の多言語化の方法はわかりましたが、アプリに表示される文字列は自らが持つリソーステキストだけではありません。 たとえば動的に変化する設定可能な項目を API で取得して、そのリストを選択メニューに表示したりします。あるいはサーバー側で定義された任意のお知らせメッセージを出したりもします。これらの中にはアプリが予め定義しておくことができないものもあります。 API から返却するレスポンスの言語をクライアントによって変えてもらうにはどうしたらいいでしょうか。それには大きく2つの方法があります。 ひとつは、ユーザーの属性として言語設定を持たせ、それをサーバーで判定して言語を切り替えること。ただしこれはユーザーがログイン済みの場合に限られ、いくつかの匿名 API(購入前お問合せとか)では対応できません。また、環境によってユーザーが言語を変えたい時(職場の PC は英語なんだけどスマホは日本語とか)なども対応が難しいです。 もうひとつは Accept-Language リクエストヘッダを利用することです。これはクライアントがどの言語を理解できるか、どのロケール(地域情報)が推奨されるかのヒントをサーバーに示すものです。一般的なブラウザや HTTP クライアントライブラリは環境から設定を読み取り、ユーザーが意識せずともすでにこのヘッダをリクエストに付加してくれている場合が多いようです。 今回は、これらをハイブリッドに対応することにしました。 すなわち、ユーザーには使用言語の設定を追加して、下記表のような選択を可能とする。且つ、それぞれの場合の Accept-Language を表のようにクライアントで付加して、サーバーでレスポンスを切り替えるようにします。 Webアプリ ユーザー言語設定 クライアント UI Accept-Language サーバーレスポンス メール、PUSH通知 システム(自動設定) ブラウザ設定に従う ブラウザ設定に従う ALに従う ALが無ければ日本語 ALがSafie非対応言語なら日本語 日本語 日本語 日本語 ja 日本語 日本語 英語 英語 en 英語 英語 ベトナム語 ベトナム語 vi 英語 英語 タイ語 タイ語 th 英語 英語 ※サーバー対応言語は日・英のみ モバイルアプリ ユーザー言語設定 クライアント UI Accept-Language サーバーレスポンス メール、PUSH通知 システム(自動設定) OS/アプリ設定に従う OS/アプリ設定に従う ALに従う ALが無ければ日本語 ALがSafie非対応言語なら日本語 日本語 日本語 OS/アプリ設定に従う OS/アプリ設定に従う 日本語 日本語 英語 OS/アプリ設定に従う OS/アプリ設定に従う 英語 英語 ベトナム語 OS/アプリ設定に従う OS/アプリ設定に従う 英語 英語 タイ語 OS/アプリ設定に従う OS/アプリ設定に従う 英語 英語 ※サーバー対応言語は日・英のみ ※モバイルのUI言語はセーフィー設定よりもOS/アプリ設定を優先 このように定義した上でデフォルト設定を「システム(自動設定)」とし、且つ「不明な場合は日本語を優先」とすることで、すでに日本語環境でセーフィーを利用している大半の既存ユーザーには影響がなく、その中でブラウザやスマホを英語やベトナム語で使っている人がいれば何もせずその使用言語に切り替わり、さらに明示的に言語を切り替えたユーザーには、メールやPUSH通知といったサービスも言語切り替えをすることができます。 翻訳について さて、言語表示仕様とテキストデータの外出しは終わったので、次は翻訳です。 まず英語は社内でなんとかなるだろうと考えました。アプリ UI に表示されるメッセージの類はある程度形式化されていますし、昨今の deepL や ChatGPT などによる AI 機械翻訳は非常に優秀と言われます。迷った時には社内にいる英語話者を頼ることもできそうです。 どうにもならなさそうなのがタイ語とベトナム語です。機械翻訳できたとしてもその精度がどうなのかまったくわかりません。 ここは素直に専門家を頼りましょう。ということで、いくつかの技術翻訳の会社に見積もりを取り、またいくつかの SaaS 翻訳プラットフォームも検討しました。その結果、ある程度老舗として実績があり、またある程度細かく相談もできそうな会社さんに翻訳依頼を出すことに決定しました。 その際、 日本語→タイ・ベトナム語よりも英語→タイ・ベトナム語の方が精度が高い というアドバイスを受け、まずは初回だけ社内で行った英訳にチェックを入れてもらい、英語からタイ・ベトナム語への翻訳依頼としています。 (ちなみにその英語チェックではほとんど赤が入らなかったので、社内+機械翻訳で充分いけるという確信を強めることができました。AIの恩恵に喜びながらも、翻訳業界は大変だろうなという思いもありつつ。。) 翻訳について・2 翻訳会社さんとのやり取りは Excel ファイルでのシンプルなものでした。 その際、 サービスで利用する固有名詞のルールをまとめて伝えることは重要です。 セーフィーであれば「『カメラ』は基本的に”Device”で表します。ただし表現的にスマホと紛らわしい場合は”Camera”も使います」とか、「 “Movie Clip” は他言語でも英語のままで」など。 また、 スクリーンショットは可能な限りすべてお渡ししましょう。 数十画面・数千ワードという翻訳になるので、ある程度は機械的な作業にならざるを得ず、すべてのサービス文脈を読み取ってもらうことは難しいですが、それでも「ここのスペースにだいたい収まるようにできませんか?」とか「英語での文字幅と同じくらいにできますか?」などのやり取りができる余地を作っていくことも非常に重要です。今回は最初に「基本的に英語でのテキスト幅を越えないようにしたいです」とお伝えしていました。 日付や通貨のフォーマットについてはどうしたら良いでしょうか。 各言語での日付表記 これについては、Google の developer documentation style guide を参考に実装しました。日本語でよく見るスラッシュ区切りの「年/月/日」は他地域ではあまり利用されないなど、様々な知見がありがたく集まっています。 また、Excel(社内では Google スプレッドシートで管理)データのソースコードへの組み込みですが、さすがにコピペではやってられません。ここはスプレッドシートから iOS/Android で必要なリソースファイル(Localizable.strings, values**/strings.xml)へ変換して書き出す Google App Script を書いて省力化しました。 Google App Script による言語リソースファイルの書き出し ただしこの方法では、言語定義の神様(マスター)が GitHub 管理の外になってしまい、メンバーが各自のブランチで神様データをエクスポートして作業すると、それらをマージするとき多重に定義が追加され、コンフリクトが発生してしまいます。 そこで、今度はリポジトリ中のリソースファイルをまとめて csv ファイルを作成し、それを Google スプレッドシートにアップロードする GitHub Actions のワークフローを書いて、リリースごとに神様データを自動更新していくことにしました。 GitHub Actions によるローカライズ対応スプレッドシートの更新 より効率的な翻訳対応にはまだ改善余地があると思いますが、一旦このような形に落ち着いています。 ちなみに翻訳依頼の頻度としては、英訳はリリースごとに社内で対応し、タイ・ベトナム語はある程度溜まってきた数ヶ月ごとに発注するとして、その間はタイ・ベトナム版には英語が混在することを許容する、といった仕様にしています。 翻訳課題:ベトナム語意外と横長問題 日本語を英語にしたとき、文字数が増えてレイアウトが横長になることへの対応が必要なことは予想していました。 その上で意外と大変だったのがベトナム語です。 日本語とベトナム語のフッタメニュー 英語よりもさらに長い表現になることが多く、QA 確認ではテキストの見切れ指摘が多発しました。 ここは、「ユーザー設定」→「設定」などテキストをシンプルにできないかを検討するとともに、レイアウトに収まらないもののいくつかは英語に戻すことで対応しました。 ここについては複数の著名なアプリをベトナム語設定で見てみて、このくらいは英語でもOKかなという肌感をひとまずの根拠としています。 メジャーなアプリのベトナム語対応例 さらに理想なのは、テキストがなくてもメニューの意味がわかってもらえることですね。ここは自らの認知度とデザイン双方に対しての自信が必要なところだとは思います。 メジャーなアプリのフッタデザイン それぞれ、どれが何のアプリか、わかりますでしょうか? 翻訳課題:テキストデザインとFigmaのローカライズ たとえば、これはあくまでも例ですが、このようなダイアログの本文をローカライズするとします。 カメラ再起動の警告ダイアログサンプル 言語リソース定義は以下のように分解すれば対応できます。 “restart_dialog_text_prefix” = “再起動中は”; “restart_dialog_text_body” = “録画が停止します”; “restart_dialog_text_suffix” = “ので、しばらく経ってからご確認ください”; 英語はこう。 “restart_dialog_text_prefix” = “”; “restart_dialog_text_body” = “Recording will be paused”; “restart_dialog_text_suffix” = “ during the reboot, so please check back after some time”; このようにして、prefix, body, suffix を結合し、 restart_dialog_text_body を赤文字のボールドで装飾するルールにすれば、近い表現の英語になると思います。 しかし、ベトナム語やタイ語ではどうなるのでしょうか。語順がどうなるかわからないし、翻訳者に細かいニュアンスを伝えることも手間がかかりそうです。 こういったことを防ぐためには、 できる限りテキストはシンプルにすること(説明無しでわかるUIが最高) テキストの一部を装飾する場合は少なくともセンテンス単位にして、一文を短くすること などを心がけることが大事になりそうです。ここまでがワンセンテンスだ、よろしいか? デザインの段階で、国際化を意識した作業ができていると良いですね。 また、今回は既存のアプリをローカライズするという要件と、スケジュールの都合もあり、レイアウト調整などの作業もすべて開発チームで行いました。が、本来は社内で英訳までするのであれば企画・デザインの段階で各言語での見た目を確認できた方が良いはずです。 過去の記事でも触れたように、モバイルチームでは デザインのやり取りに Figma を利用しています。 Figma 上でのローカライズ方法もこれから調査・検討していくところですが、 lokalise などのサービスは試してみたいなと思っています。 時間について さて、言語の話だけでずいぶん長くなってしまいましたが、お次は時間(タイムゾーン)についてです。 幸いにして、セーフィーのサービス開始当初から、データベース内の時間はすべて UTC で管理されており、また多くの時間関連 API はリクエストに timezone のパラメータを受けて、その時間帯でのレスポンスを返せるようになっていました。(先見の明!) なので、クライアントとしては基本的に現在の OS のタイムゾーン設定をリクエストパラメータに追加するだけで良さそうです。が、むしろここで悩んだのは仕様の方です。 Safie 三位一体 セーフィーのサービスを構成する基本3要素、カメラデバイス、クラウドサーバー、ビューアークライアントは、地理的にすべて別の場所にいることがありえます。それぞれに OS があり、それぞれのシステムに時間とタイムゾーン情報を持っています。 タイのバンコクに置いたカメラを、ハワイで休暇中のユーザーが見ている時、ビューアーに表示される時間はどこになるのが良いのでしょうか?セーフィーは東京に本社がある日本のサービスですが、そこから「xx時xx分にカメラが動きを検出しました」というメールが来る時、その時間帯はどこになるのが正しいのでしょうか?夜間だけ録画するようにタイマーを設定する UI の時間はどう見せましょうか? ここもおそらく議論の余地があると思いますが、結論としては クライアントの UI で表示・設定する時間はすべてクライアントの(PCやスマホに設定された)タイムゾーンに従う ただし、カメラ映像に重畳して録画される時間情報は、カメラデバイスのタイムゾーンに従う これは例えば犯罪行為のエビデンスとして映像上に表示された時刻が用いられることがあることを重視して、カメラが見ている「その場所」のタイムゾーンを優先しています メールに記載する時間については、デバイスに関連するものはそのタイムゾーンに従い、”(UTC+09)”といった注記を加える PUSH 通知に載せる時間情報は epoch ミリ秒で表し、受け取ったクライアントは自分のタイムゾーンで適切に再生処理する などのルールを定めました。 Viewerの時間表示 この対応のため、JST 固定になっていたデバイスのタイムゾーンを可変にできるよう FW を更新してもらい、サーバーとカメラの I/F やメールテンプレート生成などのロジックを変更してもらった上で、クライアントは先述の通りリクエストにタイムゾーンを付加するだけでほぼ対応が終わりました。サーバー・デバイス各チームの尽力に感謝です。このあたりの実装のお話もいつかエンジニアブログで読んでみたいところ。 ちなみに、今回はできるだけシンプルな対応にとどめていますが、タイムゾーンは突き詰めると考えなくてはならないことがたくさん出てくるようです。気になった方は有名な魔導書「 タイムゾーン呪いの書 」を読んでみてください。 おわりに 日本語と日本時間を前提としていたサービスを、国際化・ローカライズした活動について、ひとまずアプリ視点からのまとめを書いてみました。その後、面接で海外からの候補者がアプリの話をしてくれた時に、英語で何も違和感ないですよと言われてひそかに喜んだりなどしています。さらに対応を拡げていきたいですね。 文中で何度も触れているように、この PJ はモバイル以外も各チーム対応に尽力いただいたので、そちらはぜひ別記事を楽しみにしていていただきたいと思います。(ちなみに国内サービスが初めて海外展開をする際に、多くの場合最も重要なトピックは「クラウドインフラどこでどう構築する?」になると思います) グローバルを見据えたサービス開発をする方々に、参考になる情報があれば幸いです。 また、セーフィーでは一緒に世界を目指すエンジニアも募集しています!興味をもった方はぜひ こちら へどうぞ。 open.talentio.com
アバター
この記事は Safie Engineers' Blog! Advent Calendar 14日目の記事です。 こんにちは、あるいはこんばんは 第1開発部QCDグループ 小山と申します。 ブログ書きますよからもう1年、 さすがに筆を走らせないと追い込まれてしまうので… 今年もいろいろありましたが、ブログのネタにできる変化としてはテスト管理ツールの運用を始めたことでしょうか。 こちら導入時の動機、想定していたメリット・デメリットや、運用開始後上手くいった・いかなかった点について記載していければと思います。 テスト管理ツールを導入したいと考えている方にとって少しでも役立てれば幸いです。 1. 使ってみたいと思った不純な動機 2. 導入前に享受できると思ったメリット・デメリットについて 導入前想定メリット 導入前想定デメリット 3. 上長からの反応(主観的な振り返り) 4. 運用開始直後の振り返り 1. テストにまつわる工数の削減(上手くいっている) 2. テストケースの資産化(どちらかというと上手くいっていない) 3. テスト状況の可視化、レポート作成工数の削減(どちらかというと上手くいっていない) 4. BTSとの連携(どちらかというと上手くいった) 5. 管理ツール内の情報(実施結果等)を共有する場合(上手くいっていない点) 6. 入力工数の活用(上手くいっていない点、課題) 7. 自動化ツールとの連携が出来ていない(上手くいっていない点、課題) 5. まとめ 1. 使ってみたいと思った不純な動機 Googleスプレッドシートでの進捗管理がめんどくさくて仕方がない… これが私の大きな原動力でした。 というのも、弊社はスプレッドシートで作成されたテストケースを用いて定期リリースに向けたテストを実施しているのですが、 光栄なことに複数のプロダクトの担当を任せていただいていたため、 予定したテストの終了確認と日々の進捗確認が非常に手間でした。 少し細かい内容ですが、 テスト項目数が多いため縦に長く、且つ環境ごとに実施するため横にも広がり… うっかり1項目を飛ばしてしまった場合、探すのが大変という状況でした。 (〆作業を行う為のスプレッドシートとのにらめっこが嫌で嫌でしかたがなかった) ※ちゃんとリグレッションテストを改善しろよというごもっともなツッコミは無しでお願いしますmm また何度も使うテストケースなので、コピーして過去に入力した結果欄を消すのも、めんどくさくて嫌でした。 (メンテナンス工数をうまく捻出できず秘伝の継ぎ足しタレ状態に) 2. 導入前に享受できると思ったメリット・デメリットについて 紆余曲折あり、無料ツールでの失敗から始まり有料ツールのトライアル利用を経て、 まずは1年使用できることがグループ内で合意でき、上長の承認もいただけました。 ここでは、 こんなことが出来るようになるだろうなという導入前に想定していたメリットをつらつらと… 導入前想定メリット テスト管理工数の削減(1PJの定期リリース1回に際し,15分×10営業日程度を想定) テストケースの資産化、再利用ができるためテスト準備の工数削減になる テスト状況の可視化、レポート作成工数の削減 自動テストツールとの連携による、テスト実施内容の可視化 PJごとにばらつきがちなテスト粒度が、同じツールを使用することで均一化 管理ツール導入により何か全体的に改善されるのではないかという漠然とした期待 導入前想定デメリット テストケースの移行コスト(プロダクトによっては1か月弱かかる可能性も) 一度導入するとやめることが難しい、エクスポートしてから加工するなどスプレッドシートでは無かった新たなコストがかかってしまう恐れ テストケースレビュー方法の再検討(アカウント数に上限があるツールだったため) 上記を想定しメリットで納得いただけるよう準備したつもりでしたが、いまいち上手くいかず… 自戒の意味も込めて、メリットに関する反応を主観的に振り返りたいと思います。 3. 上長からの反応(主観的な振り返り) 反応〇 テスト管理工数の削減 再利用によるテスト準備工数の削減 反応△ テストケースの資産化 テスト状況の可視化、レポート作成工数の削減  ※自動テストとの連携、ばらつきについては承認時には明記せず口頭での説明  ※その他デメリットについては理解はいただけたと感じています 上長の立場を鑑みた際、 年額の費用が発生するため、想定通り社員工数の削減が強めな説得材料になりました。 導入前に予想していたよりも、テストケースの資産化は刺さらずという結果は、 説明が下手だった可能性が一因にあったと反省しています。 最終的にはQCDグループが今後も将来拡大していく旨、及び、 現場の負担が軽減されるならという理由で承認いただけました。 おまけ:グループ内で出た「スプレッドシートでいいじゃん」をあらためて振り返ると テスト管理ツールを導入したいですと希望した際、 きっと他メンバーから上がるであろう伝家の宝刀「スプレッドシートでいいじゃん」に対しての振り返りです。 当時はうまく返答できませんでしたが、今返す言葉を考えるとしたら以下になると思っています。 テストを資産として残し”やすい” (保管場所が統一される) フォーマットがある程度決まっているため、テストの実施体制を整え”やすい” (個人による差分が出来くい) テスト管理体制を工数を多く使用することなく構築し”やすい” (計算式の修正から解放される) ただ、ツールの学習コストや移行コスト、テスト設計の自由度まで鑑みてしまうと、どちらに分があるのかは導入後の現時点でも明言は出来ない状態です。 明言はできないものの、推進派一意見としては、 管理ツールを使うことにより将来的に実現したいことを考える機会が得られ、 次のステップを見据えることが出来たのでよかったと思っています。 4. 運用開始直後の振り返り ここからは、導入してどのように使っているか、活用できているか、上手くいっていない運用など、 軽く紹介できればと思っています。 導入したテスト管理ツール: Testrail ( https://www.techmatrix.co.jp/product/testrail/ ) いつも細やかなサポート対応ありがとうございます。 1. テストにまつわる工数の削減(上手くいっている) 他メンバーの協力を得てテストを行う案件が多い中、日々の進捗や成果物確認については楽になったと思っています。 プロダクトにより大小はありますが、 1PJの定期リリースに際し、【10分~20分×テスト期間の営業日】は削減が出来ているかと。 例:1クリックでステータス毎のテストケースの抽出ができる(すごく便利) ※「Pass」ステータスとなっているテストを抽出した結果 また、マスターテストケースを作れるプロジェクト形式を選択すれば、 テストの資産化(再利用または不具合流出時の過去結果の参照など)は上手くいくと思います。 <運用一例> マスターテストケース(v1.0)からv1.1ベースラインを切り出す ⇒テスト終了後、マスターテストケース(v1.0)にv1.1エンハンス個所を反映させる  ⇒次ver開発時にマスターテストケース(v1.1)からv1.2ベースラインを切り出し   ⇒v1.1実施内容も実施時のまま保管することが出来る ベースライン切り出し時に、 どのテストケースを対象とするか、及び、フィルター後も動的に更新するかなどを選択でき、 スプレッドシートでのセル削除や非表示、項目数計算式の修正等からも無縁の状態となりました。 SubVersionを利用している環境下でも同様のことが出来ますが、 「v△△時の○○というテスト結果を探す」という状況ではテスト管理ツールの方が検索性は向上していると思います。 例:次テストケース作成時における参照元の選択 例:更新したMasterテストケースから実施するテストのフィルタリング ※動的なフィルタリングができるのでテスト更新時にももれなく対応できました。 2. テストケースの資産化(どちらかというと上手くいっていない) テストケースの再利用という点はうまく運用できていますが、 たとえば過去の結果を振り返るといったような、資産化による価値はまだ享受できていません。ここは今後の課題。 3. テスト状況の可視化、レポート作成工数の削減(どちらかというと上手くいっていない) テスト終了時のレポートは、標準テンプレートを利用すればクリックのみで作成・提出することは可能です。 ただ、プロジェクトごとに欲している内容は異なっているため、上手くいっているところと活用できていないところ両方あるのが実情です。 とはいえ、直近で行われたTestRailのバージョンアップにより、 TestRailとBTS連携を行っていれば信頼度曲線をすぐに出力することが出来るため、 今後プロダクトが成熟したときに活用できるのではないかと考えています。 4. BTSとの連携(どちらかというと上手くいった) 結果入力時に欠陥にBTSのキーを入力することで、 別ページではなくテストケース上で詳細内容を確認できるようになっているため、 作業効率は上がったと考えています。 しかしながら、 テスト内容をもとにBTS側にチケットを発行できる機能もあるのですが、 フォーマット等調整中のため上手く実運用で使用できておらず、今後の課題となっています。 5. 管理ツール内の情報(実施結果等)を共有する場合(上手くいっていない点) 現在QCDグループでは、 テスト実施前にテスト観点やテスト範囲・対象の認識合わせを行ったうえで、 テストケースの作成を行い、実施しています。 テスト観点等については従来通りスプレッドシートを使用することが多いため、レビュー自体に影響はないのですが、 テストケースの展開はアクティブアカウントしか閲覧できないため、 アカウント数を最小で運用しようとすると課題になってしまいます。 現状は、非アクティブユーザーとアクティブユーザーを切り替えることで、 プロジェクト側のステークホルダーにはテストケースを共有しています。 もし費用面で制限がないのであれば、 各プロダクトの企画及び開発リーダーの2名+αとして人数に余裕のあるプラン選択にすることをお勧めします。 来年度は効果測定を実施しつつアカウントを増やし、 運用面でどのような変化が起き、開発側の反応はどうだったのかは別の機会にお伝えできたらと考えています。 6. 入力工数の活用(上手くいっていない点、課題) Testrailでは各項目実施時に工数を記録できる機能がありますが、現状はまだ活用できていません。 将来的にはテスト計画時の見積もりに活用したいと考えてはいます。 7. 自動化ツールとの連携が出来ていない(上手くいっていない点、課題) 例えば、現在モバイルプロジェクトでは毎日夜間にスモークテスト(荒めの全体動作確認)を実行しています。 理想像としては、どのrevison番号で実行してどういう結果だったかを、自動的にテスト管理ツール側に出力できるようにしたいのですが、 技術力と知見が足りず悪戦苦闘中です。 5. まとめ 導入してからの上手くいっている点、どちらかというと上手くいっている点、及び課題を簡単ではありますがまとめてみました。 正直、TestRailというツールの機能がリッチすぎて触れていない・活用できていない点が多くあり、 恥を忍んでブログを記載しています(´;ω;`) 運用ルールも、まだプロダクトごとに差分があるような導入の初期状態ですが… テスト管理ツールを導入してみた結論及び感想としては、 テスト管理に関する工数削減など、テストマネージャーといわれる職域の方の効率化の面で貢献してくれる可能性は十分にあると思います。 しかしながら、ある程度テストプロセスが成熟しているプロダクトでは、 導入時のコストを鑑みると、テスト管理ツールはあまりメリットがないように導入直後は感じてしまうかもしれません。(グループへの提案時にそう感じた) そうではなく、これから開発プロセスやテストに関して固めていくんだというフェーズのプロダクトでしたら、 上手くはまると思いますので、無料のテスト管理ツールを一度触って自身の組織に提案してみるのはどうでしょうか。 提案することで、 言語化する能力や説明力、上長が求めているもの、優先度を理解できるなど、 思わぬ副産物も得ることが出来ました。 また来年、 課題に進展があった場合か新しいネタが手に入ったら、皆様にお伝えしたいなと思っています。
アバター
はじめに この記事は Safie Engineers' Blog! Advent Calendar 2023 13日目の記事です。 こんにちは。データ分析基盤グループでデータアナリストをしているワンです。 データ分析基盤グループに関しては、下記の記事で詳しい情報が掲載されています。この分野にご関心がある方は、ぜひご一読ください。 engineers.safie.link データを活用したビジネス改善とビジネスサイドとの連携強化の観点から、私たちデータ分析基盤グループはチームにおける主要KPIのモニタリングとアクションへの結び付けを促進する文化を築くことを目指しています。 セーフィーでは、データの可視化ツールとしてTableauを採用しておりますが、その利用率とデータ活用文化を向上させるために、 適切なタイミングで 適切な人々に 適切なコンテンツを 提供することを重視しています。 Tableauで構築したVizをチームメンバーが日常的に使用するSlackに自動連携させることで、データへの迅速なアクセスが可能となり、より効率的な意思決定が行えるようになり、全体としてビジネスの成果向上を期待できます。 それを実現するために、GASでTableau VizをSlackに自動配信する仕組みを実装してみました。 この記事では、Google Apps Script (GAS)を使用して、Tableau VizをSlackに自動連携する方法を紹介します。 はじめに 準備 手順 Tableau Vizのサブスクライブ TableauのログインとVizの選択 サブスクリプション機能の設定 Slack Botの作成とトークンの取得 Appの作成 Bot機能の追加とOAuthの設定 Bot Tokenの確認 APPのインストール 投稿させるSlack Botをチャンネルに招待する Google Apps Scriptの設定 GASの新規作成 GASの編集 SLACK_TOKENをプロパティに設定する GASトリガーの設定 まとめ 準備 Tableauで配信したいVizを用意します。 Tableauのアカウントが必要です。 Slackでボットを作成し、トークンを取得します。 手順 Tableau Vizのサブスクライブ 連携したいTableau Vizを選択し、サブスクライブ機能を使って、Gmailに定期配信します。 TableauのログインとVizの選択 Tableau Cloud にログインします。 Vizの選択 配信したいTableau Vizを開き、配信したいものを選びます。 サブスクリプション機能の設定 サブスクリプション機能のアクセス ビューのツールバーで、[Watch (視聴)] > [サブスクリプション] を選択します。 サブスクリプションの詳細設定 配信するメールアドレスを選びます。Tableau登録時に使用したユーザーのメールアドレスに配信したいVizを送信します。個人メールアドレスよりは、管理面や効率的に情報伝達を考えてチームグループのメールアドレスに配信するように設定します。 配信する頻度とスケジュールを選択します。今回は毎日朝08:55で指定しています。 サブスクリプションの確認 すべての詳細を確認した後、[サブスクリプション] ボタンをクリックしてサブスクリプションを完了します。 Slack Botの作成とトークンの取得 Slackボットの作成、設定、トークンの取得、インストールを手順に従って完了します。 Appの作成 Slackの公式サイトにログインした状態で、アプリケーションページ へ移動します。 [Create New App] をクリックし、[From Scratch] を選択します。 アプリに名前と対象のSlackワークスペースを指定し、[Create App] をクリックしてアプリを作成します。 Bot機能の追加とOAuthの設定 先に作成したApp のページに移動します。このページ内で、Basic Information / Building Apps for Slack / Add features and functionality で、[Bots] を選択します。 App Homeのページに移動します。こちらで [Review Scopes to Add] を選択します。 OAuth & Permissions のページに移動します。少しスクロールして、[Scope] 下の [Bot Token Scopes]で、必要なスコープを追加します。 chat:write files:write users:read users:read.email OAuth & Permissions のページで、[Redirect URLs] を追加します。 https:// /auth/add_oauth_token を追加します。 * にはワークスペースのURLが入ります。 Bot Tokenの確認 OAuth & Permissions のページで、OAuth Tokens for Your Workspaceの下で、[Bot User OAuth Token]をコピーします。 APPのインストール [Settings] から [Basic Information] を選び、TableauInsightBot AppをSlack ワークスペースにインストールします。 投稿させるSlack Botをチャンネルに招待する 作成したBotを該当チャンネルに招待します。 [Intergrations]をクリックし、[Add apps]で先ほど作成したBotを該当チャンネルに招待します。例えば仮に #daily_notification_tableauのチャンネルに招待しました。 Google Apps Scriptの設定 GASの新規作成 Google Apps Script からProjectを新規作成します。 GASの編集 作成したGASファイルに、下記コードをコピペします。例は毎日配信のスクリプトです。 function Tableau2Slack () { // スクリプトプロパティからトークンを取得 var scriptProperties = PropertiesService . getScriptProperties () ; var SLACK_TOKEN = scriptProperties . getProperty ( 'SLACK_TOKEN' ) ; // 今日の実行日付を取得し、東京タイムゾーンにフォーマットする var currentDate = new Date () ; var formattedCurrentDate = Utilities . formatDate ( currentDate , 'Asia/Tokyo' , 'yyyy-MM-dd\'T\'HH:mm:ss' ) ; // スクリプトプロパティから前回の実行日付を取得し、フォーマットする var lastExecutionDate = scriptProperties . getProperty ( 'lastExecutionDate' ) ; var formattedLastExecutionDate = lastExecutionDate ? new Date ( lastExecutionDate ) : null ; // 今日の実行日付が前回の実行日付と同じ場合は、関数を終了する if ( formattedLastExecutionDate && formattedCurrentDate === formattedLastExecutionDate ) { console . log ( "本日はすでに実行済みです。" ) ; return; } console . log ( "Last execution date: " + lastExecutionDate ) ; // 前回の実行日付をコンソールに出力する // Tableauからの自動メール取得(条件に一致する最新のメールを一件取得) var Threads = GmailApp . search ( 'from:"[メールアドレス]" subject:"[メールタイトル]"' , 0 , 1 ) ; var messages = Threads [ 0 ] . getMessages () ; var message = messages [ messages . length - 1 ] ; // 自動メールから画像と日付を取得 var attachments = message . getAttachments () ; var date = new Date () ; date . setDate ( date . getDate () - 1 ) ; // 現在の日付から1日前の日付を取得 var time = Utilities . formatDate ( date , 'Asia/Tokyo' , 'yyyy年M月d日' ) ; // Slack API Bot設定 var data = { 'token' : SLACK_TOKEN , 'file' : attachments [ 0 ] , 'filename' : '[ファイル名]' , 'channels' : '[配信するチャンネル名]' , 'title' : time + '[Tableau Viz名]' , 'initial_comment' : '[コメント]' } ; var option = { 'method' : 'POST' , 'payload' : data } ; UrlFetchApp . fetch ( 'https://slack.com/api/files.upload' , option ) ; // スクリプトプロパティに今日の実行日付を保存する(日付と時刻を含む形式) scriptProperties . setProperty ( 'lastExecutionDate' , formattedCurrentDate ) ; } ; コードの下記の情報は適切な値に置き換える必要があります。 [メールアドレス] [メールタイトル]  [ファイル名] [Tableau Viz名] [配信するチャンネル名] [コメント] SLACK_TOKENをプロパティに設定する プロジェクトのプロパティ設定 GASメニューバーから歯車アイコンの[プロジェクトの設定]をクリックします。 [プロジェクトの設定]画面の一番下に[スクリプト プロパティ]があります。 SLACK_TOKENの追加 [プロパティ]フィールドにSLACK_TOKENと入力します。 [値]フィールドにSlackのトークン値を入力します。このトークンはSlack APIから取得したものを使用します。 入力した情報を確認し、「保存」ボタンをクリックします。 今回はスクリプト内でこのプロパティを使用しているので、 PropertiesService.getScriptProperties().getProperty('SLACK_TOKEN') を使用してアクセスします。 GASトリガーの設定 この手順に従って、GASで定期的にスクリプトを実行するトリガーを設定することができます。これにより、例えば毎日特定の時刻にデータを処理したり、定期的なTableauのVizを送信したりする自動化が可能になります。 GASのメニューバーから時計マークのアイコン[トリガー]を選択します。これにより、[現在のプロジェクトのトリガー]画面が開きます。 右下にある「トリガーを追加」ボタンをクリックします。 この画面でトリガーを設定します。 実行する関数を選択します。 今回の場合はTableau2Slack 実行するデプロイメントを選択します。 イベントのソースとして時間主導型を選択します。 頻度の設定 [日単位] [週単位]など、スクリプトの実行頻度を選択します。 さらに詳細な時間設定(毎日の特定の時間帯、週の特定の日など)を行います。 設定が完了したら、[保存] ボタンをクリックしてトリガーを保存します。 まとめ この記事では、GASを活用してTableau VizをSlackに自動連携する方法を紹介しました。SlackへのTableau Vizの自動連携が、私たちの業務スタイルに新たな風を吹き込んでいると感じています。 Tableauには既にサブスクリプション機能が備わっており、定期的にメールでのViz配信が可能です。しかし、これらはメールボックスに届くため、日々の業務の中で見落とされがちです。 Slackに連携されるTableau Vizは、日々のコミュニケーションの流れの中で自然に目に触れることが多く、必要な情報をすぐに手に入れられます。これにより、ただのオンライン上にある数字やグラフではなく、「話す」データを共有して、よりダイナミックな議論が生まれ、具体的な行動につながりやすくなります。 たとえば、KPIを示すTableau VizがSlackに投稿されると、そのスレッド内での議論で、その数値をめぐって即座に議論が始まり、必要なアクションが迅速に決定されます。これは、メールで配信されるデータではなかなか起こり得ないことだと思います。 Slackの使い勝手の良さから、データへのアクセスがより手軽で直感的になり、データ利用やTableau利用のハードルが大きく下がることが期待できます。
アバター
「この記事は Safie Engineers' Blog! Advent Calendar 8日目の記事です」 自己紹介と本日のテーマ セーフィー株式会社 開発本部 エンジニアリングオフィスの武田 智一です。 セーフィーには2023年1月に入社して、エンジニアに対する組織開発全般をおこなっています。エンジニアリングオフィス(組織開発)って何やっているの?というのはまた別の機会でお話しするとして、 今回はセーフィーのエンジニア全員が参加する 「開発本部会のやり方変えてみた」 のお話しをさせてもらえればと思います。 自己紹介と本日のテーマ 全体会議の課題 会議体の再定義 1. なぜやるのかを言語化 2. どうやって体感するか、の定義 3. アジェンダの再設定 改善ステップ(ストーリー作り) 改善ステップ 結果 まとめと今後の予定 全体会議の課題 現在セーフィーの開発本部には約90人のエンジニアが在籍しています。 この90人全員参加のオンライン開発本部会を月1回1時間実施しています。 「この90時間ははたして有効に使えているのか?」 と、エンジニアの皆さんならとっても気になりますよね。 セーフィーでも同じ状況でした。 具体的には開発本部会には以下のような課題がありました。 一方的な情報ダウンロードの会になっている 共有される情報が人によっては既知の情報だった 共有される情報が人によっては自分のやっていることとは遠い情報のため実感がわかなかった 結果として参加率も低く、全体的に効果的で効率的な会議運営ではなかった こういった課題を解決するために、今年の2月からエンジニアリングオフィスが主管となり会議体運営を見直しました。 会議体の再定義 基本的に、変更前後も会議の目的自体は変わっていません。 ただ、WhyをベースにHowを明確にしアジェンダを組みなおしました。 具体的には以下のように再設計しました。 1. なぜやるのかを言語化 セーフィーのビジョンは「映像から未来をつくる」です。そして、私たち開発本部は映像から未来をつくるを実際に体現しているエンジニア組織(創る組織)です。自分たち自身の業務やアウトプットは必ずビジョンにつながっているはず。 ただ、エンジニアの人数も多くなり、部署やグループも増えた結果、そのつながりをなかなか体感しづらい。 そこで、この開発本部会を軸に月に1度改めて自分たちがビジョン実現に貢献していることを認知する。そういう場にしたいという思いから、開催理由(なぜ)を「映像から未来をつくる」を体感するためにしました。 2. どうやって体感するか、の定義 ビジョン実現を体験するには何が必要かを考えたとき、まさに組織開発でするべきことだと整理しました。 組織開発において必要になってくるのは、同じ目標を正しく理解して、現在地を知ることから始まります。また、現在地を知った上で、どう感じているかを共有することで、お互いのことを深く知り、さらに一体感が生まれます。そういったHOWの部分を方針として明確化しました。 3. アジェンダの再設定 この方針をもとに、ベースとなるアジェンダを以下のように組みなおしました。 人により文章は捉え方が変わりますが、数値は同じです。 そこで開発本部全体をあらわす数字(数字で見る開発本部)を定義して、その数字を毎月共有することで、まずは目線をそろえるようにしました。 その数字の変化を具体的な報告で説明することで、全体から具体へのブレークダウンをしやすくしています。 参考までに、10月の開発本部会のアジェンダと数値報告の例です。 改善ステップ(ストーリー作り) アジェンダの整理までは良いですが、それだけでは会議運営の改善にはつながりません。 毎月1回=年12回の会議を連携して有効に使うために、それぞれの月でどういった状態になってほしいか、その結果全体としてどう流れを作るかを最初に設計しました。 改善ステップ 月 テーマ 狙い やること 1月 現状確認 ー 昨年と同じ形で開催 2月 認知 本部会で何を実現したいかを理解してもらう 概要説明、アンケート設計 3月 気付き ちょっと変わったかもという雰囲気をつくる 数字で見るシリーズの意味付、アンケートフィードバック 4月 自分事化 組織戦略(中身)について理解してもらう ロードマップに沿った組織戦略の発表 5月 事を知る1 各部でやっていることを知る 各部毎の発表(プロダクト詳細発表) 6月 事を知る2 各部でやっていることを理解したうえでフィードバックできる状況をつくる ガヤスレの活用、各部勉強会とのリンク作り(誘導) 7月 下期計画発表 上期の結果及び下期の目標共有 上期ふりかえり、下期注力目標 8月 人を知る1 組織にどんな人がどんな思いで働いているかを知る(人にも興味を持ってもらう) CTO発表 9月 人を知る2 他部署のことも知る 企画本部発表、部室長発表 10月 人を知る3 他部署のことも知る 営業本部発表、部室長発表 11月 全体を知る 他からどう見えているか(どう変わったか)知る CS本部発表、部室長発表 12月 年度ふりかえり 来年に向けた組織戦略を理解する 来期組織戦略発表、来期組織枠の説明 実際にはこの通りには進みませんでした。 ただ、各月毎の狙いが実現できたかを、アンケート結果等から毎回評価をおこないました。その結果を踏まえて微調整をしながら、翌月以降の個別アジェンダを整えることで、大きくストーリーを変更することなく、会議体運営の改善につなげることが出来たと思います。 結果 アンケートの結果です。 会議の満足度を100点満点として、何点だったかを毎回聞いています。 その結果、平均満足度は90点にまで上昇しました。また、参加率も大きく向上し10月の参加者数は100名を突破しました。(別部署の人も参加しているため、開発本部全員の人数よりも多くなった) 自由記述のアンケートコメントを見ても、毎月の開発本部会を楽しみにしてくれる人も増えてきたという実感があります。 まとめと今後の予定 開発本部会はみんなの満足度を上げることが目的ではありません。 映像から未来をつくる道筋を理解し、現在地を知ることでお互いが誇りをもってプロダクトを成長させ続ける。そして、開発文化を成熟させた最高の開発チームで「映像から未来をつくる」を実現することがゴールです。 そのためには足りていない部分はたくさんあります。また、組織も人もどんどん成長しています。 そういう変化も想定したうえで適切にハンドリングして、これからも開発本部全体会議を軸に情報を皆に届けられればと思っています。 なお、来年は以下のことにもチャレンジする予定です。 開発本部を表す数値のアップデート(と本部外への展開) 開発本部会以外の会議体設計との連携、ストーリー見直し ガヤ(フィードバック)の活性化 最後に、会議を変えるだけでは組織は変わりません。 ただ、変化のきっかけづくりにはなったと思います。これからもエンジニアリングオフィスとして、エンジニアみんなが生き生きと働ける環境づくりに貢献していければと思います。 最後まで読んでいただきありがとうございました!
アバター
はじめに こんにちは!Safie QAグループ(QCDグループ)のグループリーダーの山谷です。 前回はテストの業務についてのテックブログ を公開しましたが、今回はなりたちについてのお話になります。 私自身はSafieに入社するまではQAエンジニアどころかテストエンジニアとしてもキャリアがない状態で入社してます。 もし同じ状況でQAエンジニアチームを作る人達の何かの参考になれば嬉しいなと思い、ブログを書くことにしました。 テスト体制を作ろう テスト担当者を作ろう ■改善活動その1 ~顧客対応を巻き取る~ ■改善活動その2 ~バグチケットを溜める~ テストチームを作ろう まとめ テスト体制を作ろう 当時のSafieは開発メンバーがテストをほとんど実施している環境でした。 2020年はそんな状況で「QAについて何もスキルセットがない状態で入社した人間がテストチームをまずは作ることにした」年になります。 私の入社時期はちょうどモバイルアプリのリニューアルと、 SafieManager のリリースが控えておりました。 私の上司はこれらのテストを担当してくれそうな人を探していたのですが、とりあえずモバイルのテスト担当者から作ることにします。 そう、私です。 テストの項目書のひな形を渡され、こう書くんだよとナレッジ(もちろん外部サイト)のURLと開発メンバーのMTGに放り出されました。 ここから私のQAエンジニアとしてのキャリアが始まりました。 テスト担当者を作ろう サービス開発黎明期あるあるな話ですが、当時のSafieは仕様書がないものがほとんどでした。 そのため、まずはアプリでできることを書き出します。 Safieモバイルアプリの場合には ・ログイン画面 ・カメラ一覧  ・各種ボタン ・ストリーミング画面  ・デバイス設定  ・その他機能ボタン みたいな感じですね。 テスト版のアプリを使い画面と、それに設置されている各パーツについて期待動作を洗い出し、開発と期待結果とそれを導くための手順を埋めていきます。 当然ながらこの当時のテストケースはものすごく粗く、テストをしながらテスト項目を増やしたり、テスト手順、期待結果の書き換えなどもすごく多く発生します。 リリースまでにはこぎつけましたが、動作の安定性については今一つ。 今後も継続して品質改善を行うにあたっての行動に移ることになります。 ■改善活動その1 ~顧客対応を巻き取る~ リリース後しばらくはモバイルアプリの顧客サポートは基本的には私が巻き取ることにしました。(なりました。) エンドユーザーから指摘されたバグを迅速に開発に連携を行える テストケースの抜け漏れに気づくことができる 自分の対応のノウハウを他カスタマーサポートメンバーに共有ができるようになる お客様から温度感が高いご意見をいただくなど大変ではありましたが、開発メンバー内外含めてメリットはありました。 何より黎明期っぽい働き方ができたので楽しかった面もあります。 また、当時はモバイルアプリとしての不具合対応フローやノウハウがカスタマーサポート部にはあまりなかったので、そういった運用を作ったりしたのも良い思い出です。 ■改善活動その2 ~バグチケットを溜める~ 当たり前ですがその当たり前が当時はありませんでした。 バグチケットを置き場所を決めて、バグの修正プロセスを開発と話し合いリリーススケジュールを決める 改善を確認した際のチケットクローズまでのフローを立てる チケットを積み残す際の基準を作る バグチケットを参考にリグレッションテスト項目を作る など、今では当たり前にやっている運用をまずは作ることからスタートしてます。 こうしてまずはモバイルアプリにテスト担当者が一人出来上がることになりました。 テストチームを作ろう さて、モバイルがひと段落つくと次はSafieManagerのリリースがやってきます。 SafieManagerは開発メンバーがテストケースを作成し、私はテスト担当としてプロジェクトに組み込まれました。 SafieManagerはSafieのWebアプリケーションの中ではかなりシステムが複雑であったため、テストケースもモバイルに比べると倍以上ありました。 開発側もテスト実施にお付き合いいただけることになったのですが、さらに元QAエンジニアでSafieViewerのテストを実施されていた社員1名がアサインされます。 だいたいこの辺りからテストをやってくれる人達として認知されていくようになっていきます。 併せて全プロダクトへのテスト参加を目指して人員の拡大を進めていくことなり、僕+社員1名、業務委託1名~2名でQAチームとして業務に取り組むことになります。 また、テストのことを少しでも学ぼうとJSTQBを取得したのもこのあたりだった気がします。 当時のなんとなくの役割 全PJの要件定義の調整+テストの全体スケジュールの調整+テスト作成+テスト実施 山谷 Safie Viewerや、山谷が持ちきれないもののテストプロセス管理および実施 社員 テスト実施のサポート 業務委託 さて、上のような体制で半年ほど立つと、1つの課題が生まれてきました。 気づくと毎週私のMTG時間が30時間を超えています。 社内PJ全てに参画した結果、1週間の予定がほぼMTGで埋まってしまうことになりました。 頼っていただけるのは嬉しい反面テスト作成や、実施などはMTG中に内職をするか残業がマストになってます。 この課題を解決するにあたり、要件定義から入れるエンジニアを増やすことにします。 採用基準の中にプロダクトの品質や開発プロセス改善に取り組んだことがある、もしくは取り組みたいと考える方を追加し、こちらからの面接の内容もそれに則った内容に変更しました。 1年くらい上記で採用を続けた結果の所感ですが、以下のようなことを感じてます。 QAエンジニアはかなりジョブ定義が曖昧な職である ほとんどの場合はテストエンジニアやテストマネージャーといった経歴の方が多い ビジネス、企画、運用などを包括的な目線をもってプロセス改善に取り組んでいただけるか否かは当人の意識や資質に依存しやすく、こういった経験を持っている方は非常に少ない また、上記のようなスキルを持った方であっても芽が出るまでにはある程度の期間が必要 といった部分です。 そのため、最近では品質改善をどのように改善してきたか、もしくは課題をお伝えしたうえでそれをどのように改善したいと考えるか、などの質問を増やし社内でご活躍いただけるか否かの解像度をあげるように取り組んでいます。 まとめ まとめに入っていきますが、現在は社員6名、業務委託5名の11名となり、グループとなるまで拡大することができましたが、来年度は15名程度までは拡大したいと考えます。 Safie のQAグループのいいところはプロダクトの魅力と、裁量の広さです。 「良い品質のアプリケーションを作るだけではなく、良いサービス/プロダクトを作っていく」ことに共感をいただけましたら、ぜひ応募をいただければ嬉しいです。 最後までお読みいただきありがとうございました。
アバター
この記事は Safie Engineers' Blog! Advent Calendar 2023 4日目の記事です。 はじめに システム概要 システム構成 Stable Diffusion ChatGPT FastAPI Poetry 実装 プロジェクトの作成 ファイル一覧API API作成 URLのパスでディレクトリを指定できるように修正 ファイルのダウンロード対応 ファイル一覧ページ 画像のメタデータ取得 画像一覧ページ 画像のメタデータの表示 まとめ はじめに こんにちは、開発本部の中村俊成です。 今年も色々と流行りの技術がありましたね。その中でも生成AIは注目度が高かったと思います。私も生成AIの社内活用を検討する過程で、業務や個人的なプロジェクトにおいてChatGPTやGitHub Copilotなどの生成AIを徐々に取り入れています。 そうした中、Stable Diffusionで生成した画像とプロンプトを一緒に表示できるツールが欲しくなり、ChatGPT(GPT-4)の助けを借りて簡単なものを作成しました。また、セーフィーのサービスの一部で使用しているFastAPIフレームワークの基礎を学ぶ良い機会でもあったので、このツールの開発にFastAPIを使用しました。 この記事では、その開発過程を紹介します。 以下のような方の参考になれば幸いです。 何かを作りたい、または新しい技術に触れたいと思っているが、なかなか始められない方 私のことですmm ChatGPTの使用例を探している方 シンプルな指示を使った例になりますので、プロンプトエンジニアリングのサンプルとしては物足りないかもしれません FastAPIを使用したシンプルなサーバーの実装例に興味がある方 ここで紹介するのは実験的な実装ですので、参考にされる場合は注意してください システム概要 PC上で、Stable Diffusionで生成した画像を、画像サーバーに保存しておき、スマホや他のPCから閲覧できるようにします。画像サーバーの実装は、ChatGPTに相談しながら行いました。 システム構成 システム構成 Stable Diffusion Stable Diffusion は、Stability AI 社が開発している、ユーザーが与えた指示(prompt)を元に画像を生成するAIモデルです。 Stable Diffusion で画像を生成するためのツールとしては、 stable-diffusion-webui を利用しました。 stable-diffusion-webui で生成した画像には、生成時の prompt がメタデータとして記録されています。 ChatGPT ChatGPT は、Open AI社が開発している、人工知能に基づいたチャットボットで、テキストによる会話ができます。ユーザーのリクエストに応じて、情報を提供したり、創造的な内容を生成することが可能です。ブラウザや専用のアプリケーション、APIを通じて利用可能です。 FastAPI FastAPI は、Pythonの標準である型ヒントに基づいてPython 3.6 以降でAPI を構築するための、モダンで、高速(高パフォーマンス)な、Web フレームワークです。 Poetry Poetry はPythonのパッケージ管理ツールの一つです。pyproject.toml ファイルを使用してプロジェクトの依存関係を管理します。 実装 実装したコードは こちら から確認できます。 では、実装の過程を紹介していきます。 プロジェクトの作成 まずはプロジェクトの作成をChatGPTにお願いしてみます。 プロジェクトの作成 指示に従って、作業を行いました。 サーバーが起動し、ブラウザで開けるようになりました。 Hello World ファイル一覧API API作成 最低限の機能として、画像ファイルの一覧が必要だと考えました。 ファイル一覧API作成 URLのパスでディレクトリを指定できるように修正 それっぽいコードが出力されましたが、ディレクトリが指定できなさそうに見えます。修正を依頼しました。 ファイル一覧APIの修正 これでコード上のBASE_DIRECTORYとURLのパスでディレクトリを指定できるようになりました。BASE_DIRECTORYにプロジェクトのディレクトリ以下にある static ディレクトリを指定して、サーバーを起動してみると、ファイル一覧が取れました。 パス レスポンス /files/ ["/Users/t-nakamura/Study/src/image-server/static/.gitkeep"] ファイルのダウンロード対応 ファイル一覧に表示されたファイルのダウンロードに対応しようと考えました。 ChatGPTが提示するコードを見ながら、以下のような要望を出して、試行錯誤を行いました。 subdirectory_name に ../ のような相対パスの指定がされたら無視するようにしたいです。 APIで返すファイル名は、BASE_DIRECTORYからの相対Pathにしたいです APIが返すレスポンスを以下のようにして、ディレクトリのリストも返したいです。 {"files": ["file1", "file2"], "dirs": ["dir1", "dir2"]} /files/{subdirectory:path} APIで返したファイル名を指定したら、そのファイルをダウンロードできるようにしたいです。 /files/{subdirectory:path} と /download/{filepath:path} を統合して、指定したpathが、ディレクトリだったら、ファイル一覧を返して、ファイルだったらファイルをダウンロードできるようにしたいです。 ファイルが画像の場合だけ、ファイル一覧で返したり、ダウンロードできたりするようにしたいです。 あいまいな表現が含まれていますが、ChatGPTが適切に解釈して、最終的に以下のようなコードを出力してくれました。 ディレクトリ内のファイル一覧表示と、ファイルのダウンロード対応 サーバーの実装に反映させ、以下のようにJSONを返せるようになりました。また、画像ファイルをのパスを指定するURLを開いた場合は、ブラウザ上で画像を表示できるようになりました。 パス レスポンス /files/ {"files":[],"dirs":["2023-11-29"]} /files/2023-11-29 {"files":["2023-11-29/2023-11-29 10.02.37.png"],"dirs":[]} ファイル一覧ページ JSONで返すと、ディレクトリやファイルをブラウザ上で選択して開くことができません。そこで、簡易的なHTMLでファイル一覧ページを返すようにしようと考えました。 ファイル一覧ページの実装方針 ChatGPTからの返答が長かったので、上記のスクリーンショットでは割愛しましたが、サーバーのコード修正や、テンプレートファイルの内容が提示されました。提示された実装例に従って、HTMLを返せるようになりました。( 対応時のコミット ) パス スクリーンショット /files/ /files/2023-11-29 /files/2023-11-29/img.png 画像のメタデータ取得 ここで、画像のメタデータを取得する方法について確認しておくことにしました。 メタデータ取得APIの実装 Pillowライブラリを使えばできそうなことが分かりました。ライブラリをプロジェクトに追加し、提示されたコードで metadata 取得APIを実装すると、メタデータを取得できるようになりました。 パス スクリーンショット /metadata/img.png 画像一覧ページ データ取得に関しては、要素が揃ったので、表示部分の実装を行うことにしました。一覧性を高くするために、グリッド上に画像を表示することにしました。サーバーの実装と切り離して、シンプルな実装を知りたいと考えて、これまでとコンテキストを変えるため、新しいチャットを始めました。 画像一覧ページ 提示されたコードを、テンプレートの file_list.html に反映すると、画像がグリッド状に表示されるようになりました。 パス スクリーンショット /files/2023-11-29 画像のメタデータの表示 画像のメタデータを表示できるようにします。画像の下に表示する方式も試したのですが、画像の一覧性が下がるため不採用にしました。画像の右下にボタンを配置して、ボタンをクリックすると、モーダルでメタデータを表示できるようにしました。 メタデータのモーダル表示 提示されたHTML、CSS、JavaScriptのコードを、少し調整して、テンプレートの file_list.html に反映すると、意図通り動作するようになりました。( 対応時のコミット ) 完成版 まとめ ChatGPTにざっくりとした指示を出すだけで、動くものがすぐに仕上がるのは、驚きでした。動くものがあることで、機能を即座に試すことができますし、次に「ここをこうしたい」といったアイディアが浮かび、徐々に自分の理想に近づける原動力となりました。その結果、「あれば便利だろうな」と漠然と考えていたものを、実用的な形で素早く作成できました。 業務においても、社内外向けサービスのプロトタイプを作成する際などに活用できるという実感が得られました。今後も、この実感を基に、生成AIを生産性の向上や、新しい価値の創出に活かしていきたいです。
アバター
この記事は Safie Engineers' Blog! Advent Calendar 2日目の記事です。 セーフィー株式会社でテックリードをやっております鈴木敦志です。 セーフィーはクラウドカメラのSaaSを提供しており、現在22万台程度のデバイスに対してカメラ映像をクラウドから視聴する機能を提供しています。 それに加えエンタープライズ向けの権限管理機能や社内向けの販売管理ツールなど複数のサービスを運営しており、各サービスでMySQLのDBを共有しているためDBのテーブル数が肥大化し構造がわかりにくくなり、新機能開発の妨げとなっていました。 本稿ではデータベースのドキュメンテーションツールである tbls を導入し、DBスキーマ管理ツール skeema 、ドキュメント生成ツール mkdocs 、Github Actionsなどと組み合わせてスキーマ管理からドキュメント生成までをやっていきます。 現在の問題点 ドキュメント記述方法の検討 - tbls ドキュメント生成手順 skeema によるDBスキーマの適用 tbls によるドキュメント生成 mkdocsによるHTML生成 ドキュメント配信 tblsのViewPoints機能によるドキュメントの改善 今後の課題 .tbls.yml のエディタ補完 現在の問題点 セーフィーでは主にDBにAurora MySQLを利用しており、現在のテーブルはシャーディング用途のものを除くと452、すべて含めると5,718個あります。 各テーブルにはコメントや外部キー制約がなく、テーブルの用途を把握するにはプログラムコード内のSQLをすべて調べる必要がありました。 この問題の解決策として別途DBとコードの分割を行っており、並行してDBのドキュメント記述の仕組みの整備を行っていきます。 ドキュメント記述方法の検討 - tbls ドキュメントの記述方法は以下の方針で検討しました。 テーブル定義からドキュメントを生成する 上記に加え別途テーブル/カラムに対するコメントや外部キー制約を記述できる テーブル定義 (今回はGithubで管理されたSQLファイル) と近い場所でドキュメントを管理できる ドキュメントをCIで自動生成する これらの要件を満たすツールとして SkeemaSpy などを検討し、CIとの親和性やyamlによる書きやすいDBメタデータ記述などを決め手に tbls を選択しました。tblsはMarkdownでドキュメントを出力するためHTMLページに変換する必要がありましたが、それには mkdocs を利用することにしました。 下図が出力例です。(サンプルのデータベースを使用しています) tbls + mkdocs出力例 ER図には外部キー制約またはtbls設定ファイル (.tbls.yml) に記載されたリレーションが表示されます。 tbls + mkdocs出力例 (ER図) ドキュメント生成手順 skeema によるDBスキーマの適用 セーフィーではDBスキーマを CREATE TABLE 文の記載されたSQLファイルの形式で記述し、skeema ( https://www.skeema.io/ ) で管理しています。 ドキュメント生成のワークフローではローカルまたはGithub Actions上で起動したMySQLサーバーに対してスキーマを適用します。 tbls によるドキュメント生成 先述した tbls ( https://github.com/k1LoW/tbls ) ではMySQLデータベースおよび設定ファイル (.tbls.yml) をもとにMarkdownドキュメントおよび図表を出力します。 DBドキュメントを記述する際には簡単な内容であればMySQLのテーブル/カラムコメントを追記、Markdownで記述が必要なような複雑な内容であれば設定ファイルに説明を記述しています。 mkdocsによるHTML生成 mkdocs ( https://www.mkdocs.org/ ) はpythonで実装されたMarkdownファイルからHTMLドキュメントを生成するソフトウェアです。 様々なテーマや拡張機能が用意されていて、今回は下記の構成をとりました。 mkdocs-material: 高機能なテーマ シンタックスハイライト ( https://squidfunk.github.io/mkdocs-material/reference/code-blocks/ ) mermaid.js統合 ( https://squidfunk.github.io/mkdocs-material/reference/diagrams/ ) glightbox: 画像拡大プラグイン。関連の数によってはER図が大きくなるため採用 現在下記 mkdocs.yml を使用しています。 site_name : "Northwindデータベース定義" theme : "material" plugins : - "search" - "glightbox" extra_css : - "stylesheets/extra.css" markdown_extensions : - pymdownx.highlight : anchor_linenums : true line_spans : __span pygments_lang_class : true - pymdownx.inlinehilite - pymdownx.snippets - pymdownx.superfences : custom_fences : - name : mermaid class : mermaid format : !!python/name:pymdownx.superfences.fence_code_format ドキュメント配信 生成されたHTMLドキュメントをAWS S3にアップロードし、Google WorkspaceのOpenID Connect認証をかけて配信しています。 tblsのViewPoints機能によるドキュメントの改善 tblsのテーブルコメントおよびカラムコメントだけでも一定の役にたちますが、そもそものテーブル数が多いため探しているテーブルを見つけるのが難しいという問題があります。 これに対してtblsにViewPointsという機能が実装され、ある関心ごとにテーブルをグルーピングしドキュメントを構成することができるようになりました。これにより「契約情報」「カメラ設定」などの関心ごとにテーブルの概観と関連を把握することができ、全体感を把握することが容易になりました。 ViewPointsは .tbls.yml に下記のように記述します。 viewpoints : - name : "商品情報" desc : | 各商品は `Products` テーブルで管理され、関連するカテゴリ情報 `Categories` とサプライヤ情報 `Suppliers` の情報を持ちます。 tables : - "Products" - "Categories" - "Suppliers" tbls + mkdocs出力例 (ViewPoints) 今後の課題 上記の実装によりデータベースドキュメントの生成に対応しました。現状はコメントが付与されているテーブルはごく一部であり、今後はテーブルのコメントを充実させていく必要があります。 .tbls.yml のエディタ補完 .tbls.yml にデータベースのドキュメントを記述していく方針ですと、フィールド名やcardinalityの値などでエディタの自動補完が効くと便利です。 現状エディタ拡張などはないので、ひとまずVSCodeの YAML Language Server とtblsのソースコードから生成したJSON Schema ( https://gist.githubusercontent.com/suzuki-safie/2b56ab4771134d3c801dcd497677f53d/raw/tbls-config.schema.json ) を利用することである程度の補完とバリデーションを実現しました。 利用するには YAML Language Serverの導入後、以下の設定をVSCodeの設定に追加してください。 { // ... "yaml.schemas" : { "https://gist.githubusercontent.com/suzuki-safie/2b56ab4771134d3c801dcd497677f53d/raw/tbls-config.schema.json" : [ ".tbls.yml" ] } }
アバター
この記事は Safie Engineers' Blog! Advent Calendar 1日目の記事です。 こんにちは。セーフィー株式会社でバックエンドエンジニアをしている河津です。 アドベントカレンダーついに始まりましたね。初日の記事なので少し緊張します。 今年も様々な領域の記事がたくさん公開されると思いますので、是非他の記事も楽しみにしてくださいませ。 さて今回は、WebSocketを用いているサーバ側のアプリケーションの動作確認にwscatというツールが便利だったよという話をします。 背景 wscatとは 動作確認してみる 最後に 背景 セーフィーにはカメラの映像を視聴することができるSafie Viewerというプロダクトがあります。(デモ画面は こちら 。) このプロダクトの中で、映像のサムネイルを数秒おきに更新している箇所があり、更新の手法としてWebSocketでの通信を用いています。 具体的には、サーバ側で数秒間隔でカメラの映像からサムネイルを作成し、サムネイル画像のバイナリをWebSocketでクライアント側に送信します。 クライアント側としては都度新しいサムネイルをリクエストする必要が無いようになっています。 この仕組み周りに修正を入れた時に、WebSocket周りの動作確認を行うために良い方法を調べたところ、wscatというツールを知りました。 wscatとは wscat とはWebScoketのサーバを立ち上げたり、クライアントとして利用することができるツールです。 Node.jsが入っていれば npm コマンドでインストールすることができます。 $ npm install -g wscat 試しにサーバを立ち上げ、クライアントから通信を行ってみましょう。サーバを立ち上げるには以下のコマンドを実行します。(今回はポート3000で立ち上げてみます。) $ wscat -l 3000 -l オプションをつけると、指定したポートでWebSocketを受け付けることができるようになり、サーバーの役割を果たします。 クライアント側からの通信は以下のコマンドで行います。 $ wscat -c ws://localhost:3000 # ws://を省略しても繋がります。 $ wscat -c localhost:3000 # TLSを使った通信であればwss://と明示的に指定します。 $ wscat -c wss://example.com:443 -c オプションの後に接続したいサーバのエンドポイントを指定することで、サーバと接続されます。 それぞれ別のターミナルから実行すると、ポート3000でハンドシェイクされ、サーバ・クライアント間でメッセージを送り合うことができます。 動作確認してみる サーバ側で実装したWebSocketのエンドポイントを指定して動作確認を行うには、上記例のうちクライアント側からの通信を行う -c オプションを使用します。 リクエストヘッダを -H オプションで指定できるので、WebSocketの通信に使用する Sec-WebSocket-Key 、 Sec-WebSocket-Version などのヘッダ情報や、Authorizationヘッダによる認証情報の送信、Cookieの送信なども可能です。 この辺りの使い方はcurlに近く、個人的にかなり使いやすく感じています。 TLS利用、Bearer認証を行う前提とすると、実行サンプルとしては以下のような形になります。 $ wscat -c wss://{WebSocketエンドポイント} \ -H 'Sec-WebSocket-Key: {任意の値}' \ -H 'Sec-WebSocket-Version: {執筆時点では13}' \ -H 'Authorization: Bearer {アクセストークンなど}' 最後に 本記事では触れませんでしたが、逆にクライアント側でWebSocketを用いた実装を進めている際に、サーバ側の開発を待たずに -l コマンドで立てたサーバと疎通しながら実装を進めることもできそうですね。 WebSocketを使ったアプリケーション開発を行っている方は、ぜひ参考にしていただけると幸いです。
アバター
こんにちは。フロントエンドエンジニアの江守です。 映像閲覧WebアプリであるSafie Viewer(以下Viewer)の開発をしています。 本記事では2023年6月に「 Safie Pocketシリーズ 」で利用可能となった遠隔臨場モードについて紹介したいと思います。 遠隔臨場モード機能とは 対応の背景 グループコーディング まとめ 遠隔臨場モード機能とは 遠隔臨場モード機能とは、「遠隔臨場」での実施記録撮影時に、監督員などのワイプ映像をキャプチャ内に含めた保存ができる機能となります。 具体的には、下記手順にて有効となり、スクリーンショットで撮影いただくことで実施記録として保存することができます。 デバイス設定の設定画面で、「遠隔臨場モード」をONにします。 Viewerの音声通話を実行し、カメラのON/OFFボタンをONにするとViewer画面上にワイプ映像を表示します。 対応の背景 「Safie Pocketシリーズ」を販売していく中で、いくつかの地方自治体では以下のような独自要件があり、導入が難しいケースがあることが判明しました。 受注者は遠隔臨場が行われた記録として、全景と近接の映像をスクリーンショット等で撮影 記録の撮影時には必ず監督員の映像を含めて撮影 (出典: 石川県 建設現場における遠隔臨場に関する試行要領 概要版 ) 「Safie Pocketシリーズ」を使いながら遠隔臨場を行う際は、現場サイドでは携帯しているデバイスの音声通話を使い、監督員サイドではViewerの音声通話を使うケースがほとんどかと思います。 要件1つ目については、監督員サイドで音声通話中にスクリーンショットを撮ることで問題はないのですが、要件2つ目については、監督員の映像を表示することが出来ていなかったため対応を行いました。 グループコーディング 弊社フロントエンドエンジニアチームでは、要件やデザインが固まり実装フェーズに移行、または開発がある程度進んだ段階でグループコーディングを行っています。 グループコーディングとはVisual Studio Codeの拡張機能であるLive Share機能を使って、チーム全員でコードの共同編集を行うことです。 グループコーディングを導入した経緯としては下記があります。 各メンバーの習熟度向上 ViewerはフレームワークにAngularを利用しているのですが、長く利用し続けているメンバーもいれば、入社してから初めて利用するメンバーもおり、習熟度に差があります。 習熟度の高いメンバーからアドバイスを受けることで、習熟度の向上を図ることができます。 ソースコードの品質向上 常にレビューしながら開発を進めるため、実装方法の間違いや記載ミス、おかしな変数名があったらその場で修正することができ、ソースコードの品質を向上させることができます。 本開発でもグループコーディングを行い、ビューとロジックが一緒くたとなっている箇所があったので分割したり、不必要な記載を削除することで可読性、コードの品質を向上することができました。 また、Angular 15から安定版になったStandanlone Componentsという新しい機能を採用しました。 汎用モジュールが不要となるためコードの記載量を減らすことができ、スッキリと見通しの良いコードになりました。 まとめ 建設業界では2024年4月から働き方改革によって時間外労働が厳しくなり、遠隔臨場での現場確認の必要性が増してくるのではと考えております。 Pocketシリーズを遠隔臨場でご活用していただくためにも、今後も引き続き改善を進めていきます。 また、お客様の困りごとにスピード感を持って対応するためにも、グループコーディング等の様々な取り組みを行いながらエンジニアの習熟度、ソースコードの品質向上に努めていきます。
アバター
フロントエンドエンジニアの阿部です。 2023年7月に開催されたDeveloper Boost 2023で登壇をしてきました。 Developer Boostは登壇者も聴講者も30歳以下の若手デベロッパーを対象としたカンファレンスです。 event.shoeisha.jp 初めての登壇で大きなイベントということで緊張して臨んだのですが、何事もなく終わることができてホッとしています。 今回はこの登壇について振り返っていきたいと思います。 登壇内容 スライド 内容 きっかけ 資料の作成 発表練習 本番 今回のイベントに参加してみて 登壇内容 スライド 内容 未経験からエンジニアになった経験を元に今までどのようなキャリアを歩んできたのか、またその中で感じた悩み、今後のキャリアプランまで赤裸々に語りました。 最も伝えたかったことは以下になります。 今まで3社経験してきて結局どんな会社にいても悩みが消えることはないと感じました。なので、 悩みを深く考えすぎずに日々エンジニアとして成長していくことが、結局のところ最も大切なことであるということです。 この考えに至った経緯など詳細は以下の記事を覗いてみてください。 codezine.jp きっかけ 「こんなイベントがあるんだけど登壇してみない?」と上司の方からお話を頂き、自分からだとなかなか登壇しようとならないし、いい機会だからやってみようということで勢いでこの話を受けました。 資料の作成 キャリアよりのイベントということでキャリアについて30分話すことになりました。 「30分も何話そう...」と悩みましたが自分が話せるのは「未経験からエンジニアになった経験」だと思ったのでこの内容で発表することを決めました。 試行錯誤しながらなんとかスライドを作り上げました。 発表練習 時間を測りながら1人で黙々と喋るという練習を行ったり、 社内リハーサルとしてイベントの企画の方々の前で発表練習をしたり、色々と練習しました。 リハーサルで出た課題を修正しつつ、本番までの準備を整えました。 本番 話し始めたら意外に緊張せず、多少は上手く話せたのかなと感じています。 同じ悩みを抱えた同年代の若手エンジニアが質問ブースに何人か足を運んでくださり、「こんな時どうしてました?」「どうやって悩み解決していました?」など色々と質問を頂きました。 特に「他人と比較してしまうことに悩んでいます。」という悩みを持っている方が多い印象でした。 他人との比較ではなく、過去の自分と現在の自分を比較することに注力することが大切ということをアドバイスをさせて頂きました。 結構同じ悩みを抱えている人も結構いるんだなという印象です。 また他の登壇者の方々の発表を聞かせて頂きましたが、皆さん面白い内容が多く勉強になりました。 今回のイベントに参加してみて 参加してよかったというのが率直に感じたことです。 登壇という経験が出来たこともそうですが、懇親会では同年代のエンジニアの方々と開発している時の苦労であったり、考えているキャリアのことであったりと、色々と共有できて楽しかったです。 また是非機会があれば色々なイベントに参加していきたいなと考えています。
アバター
企画本部 AIソリューション部 今野です。 Safie Developers のPdMを担当しています。 過去に「 Safie APIの始め方と動作方法の紹介 」にて、Safie API β版の使い方を紹介させて頂きました。投稿から一年半以上経過する訳ですが、Safie APIはSafie Developersと同時にバージョンアップされ、現在はSafie API v2(正式版)として無事に公開することができています。過去の投稿の中ではβ版を正式版とすることが業務目標とお伝えしており、それを達成してこうしてまたBlogを投稿することができて嬉しい限りです。 本投稿では、Safie API v2(正式版)より提供を開始した無料トライアルを実際に使用してみた手順について紹介します。よろしくお願いいたします。 Safie Developers, Safie APIとは? トライアルに必要なものを揃える Safie APIをトライアルで動かしてみる ①Safie Developersでアプリケーションを作る ②APIキーとアプリケーションコードを取得する ③デバイス連携をする ④Safie APIでデバイス一覧を取得する ⑤Safie APIで画像を取得する 最後に Safie Developers, Safie APIとは? Safie Developersは、以前はSafie APIの紹介と問い合わせを受けるのみのサイトでした。現在は、ログイン機能が用意され、Safie APIの利用に必要な情報取得や各種設定ができるポータルサイトとなっています。 developers.safie.link Safie APIは変わらず、セーフィーが提供するクラウド録画サービスのデータを、外部のサーバーから取得したり、情報を書き足したりする機能を提供するものとなります。サーバーからの呼び出しを原則としているため、ブラウザから呼び出したい場合は Safie SDK(JavaScript) をご利用頂くこととなります。Safie SDK(JavaScript)については、改めて別の投稿で紹介したいと思っています。 Safie APIのv2(正式版)は、β版の認証情報は使用できない、メソッドが変更されている、等、様々な違いがあり、互換性は維持されていません。β版との互換性を維持しないことによる利用者のデメリットは理解していましたが、β版運用時に明らかとなっていた技術負債は軽視できるものではなく、正式版リリースを機に負債を解消して運用の安定化を行う決断をしました。正式版と銘打ってリリースした以上、今後は可能な限り仕様を公開し、後方互換を保って運用していく計画です。ご理解頂ければと思います。 互換性がなくなった分、β版ではできなかったことを大幅に盛り込みました。盛り込んだ推しの要素は以下となります。 クラウド録画サービスを契約しているだけでSafie APIのトライアルができる 認証に必要な情報がSafie Developersから待ち時間なしで取得できる Safie APIに関する各種設定がSafie Developersから行うことができる 認証方式を「OAuth2.0認証」に加え「APIキー」方式を利用できる β版では非公開だったRatelimitやエラー情報、など、機能制限の情報を確認できる β版では法人向けのみ公開だが、法人・個人の両方が利用できる 他にも様々な便利になった点があります。詳細は Safie Developers のサイトにてご確認ください。 トライアルに必要なものを揃える 早速ですが、トライアルに必要な環境を揃えていきます。 クラウド録画サービスを契約をしたカメラ(自分がオーナーのもの) Safie Developersへの登録 クラウド録画サービスを契約したカメラは、シェアされたものではなく、自分がオーナーとして契約したカメラであることが条件となっています。また、Safie Developersへの登録は以下の手順にて行います。 Safie Developers の右上の「ログイン」ボタンよりログインする ※アカウントはSafie Viewerと同じもの ディベロッパーとしての情報を入力し、利用規約を確認の上、登録する これにて準備は完了です。 Safie APIをトライアルで動かしてみる 私が具体的に動かした手順を紹介していきます。Safie APIはREST APIであり、多種多様な環境での呼び出しが可能なものです。今回の動作手順はあくまで一例にすぎませんが、動作理解の参考にして頂けると幸いです。環境の影響はほぼないかとは思いますが、動作環境を以下に記載しておきます。 MacBook Pro (13-inch, M1, 2020) macOS Ventura Version 13.4.1 (c) Google Chrome Version 116.0.5845.187 (Official Build) (arm64) Postman Version 10.17.3 https://www.postman.com/ ①Safie Developersでアプリケーションを作る まずはアプリケーションを作成しました。アプリケーションは自分が作成するサービスを表すものになります。アプリケーションを作成する際には、認証方式を選択することが必要となります。認証方式はOAuth2.0認証とAPIキー認証の2方式を提供しており、トライアルが実行できるのはAPIキー認証方式のみとなります。認証方式の違いに詳しく知りたい方は、Safie Developersのチュートリアルの 「 OAuth2.0認証 」「 APIキー認証 」をご確認ください。 Safie Developersにログインした後、「①-a 管理ツール」「①-b APIキー認証」「①-c アプリケーション新規作成」の順に選択しました(図1)。 図1. Safie Developers APIキー認証アプリケーション一覧(アプリケーション作成前) 「アプリケーションの新規作成」の画面にてアプリケーションの情報(表1)を入力し、「この内容で新規作成」を選択しました。 表1. アプリケーション新規作成 アプリケーション情報参考 項目名 パラメータ アプリケーション名称 テスト プラン トライアル 公開状況 非公開 利用サービス> 利用API 画像取得にチェック 「この内容で新規作成」を選択した後、「新規作成完了」のモーダルが出力されました(図2)。次にAPIキー作成の画面に進むため、「①-d APIキー管理に遷移する」を選択しました。 図2. Safie Developers アプリケーション新規作成完了モーダル ②APIキーとアプリケーションコードを取得する 前述手順を行うと、「APIキー認証アプリケーション>APIキー管理」の画面が表示された状態になります。ここからAPIキーとアプリケーションコードを取得し、記録しました。APIキーは、Safie APIを使うために必要な認証キーです。アプリケーションコードは、カメラにアプリケーションを認識させて、連携させるために使うコードとなります。 「APIキー認証アプリケーション>APIキー管理」の画面にて「②-1 APIキー新規発行」「②-2 発行」を選択し、APIキーを発行しました(図3, 図4)。「②-3 APIキー コピーアイコン」を選択し、APIキーを手元に記録しました(図5)。 図3. Safie Developers APIキー認証アプリケーション設定>APIキー管理画面(APIキー発行前) 図4. Safie Developers APIキー認証アプリケーション設定>APIキー管理画面 APIキー新規発行モーダル 図5. Safie Developers APIキー認証アプリケーション設定>APIキー管理画面(APIキー発行後) 「②-4 APIキー認証」を選択して「APIキー認証アプリケーション一覧」の画面に戻りました。「②-5 アプリケーションコード コピーアイコン」を選択して、アプリケーションコードを手元に記録しました(図6)。 図6. Safie Developers APIキー認証アプリケーション一覧(アプリケーション作成後) - 1 ③デバイス連携をする カメラの所有者側の立場で、②で記録したアプリケーションコードを使って、カメラにアプリケーションを認識させました。その後、サービスを作る側の立場でアプリケーションの利用を承認しました。 本来は、自分以外のカメラの所有者にアプリケーションコードを渡して、アプリケーションを認識してもらい、作成したサービスと連携してもらうのが正規の使い方です。今回は簡易なトライアルなので、アプリケーションを使うカメラの所有者側とサービスを作る側の両方を自分が兼ねています。 Safie Viewer を開き、「③-1 ユーザ設定」「③-2 Developers連携」の順に選択し、Developers連携の画面を開きました。「③-3 アプリケーションの追加」を選択し、「アプリケーションの追加申請」のモーダルを開きました(図7)。「③-4 アプリケーションコード」の入力枠に、記録していたアプリケーションコードを入力し、「③-5 次へ」を選択しました(図8)。「③-6 デバイス選択」にてアプリケーションを認識させたいカメラを選択しました(図9)。その後、申請内容確認画面が表示され、内容確認の上、「申請」を選択しました。 図7.Safie Viewer Developers連携 図8.Safie Viewer Developers連携 アプリケーションの追加申請モーダル 図9. Safie Viewer Developers連携 アプリケーションの追加申請モーダル カメラがアプリケーションを認識した上で、最後にサービスを作る側が承認を行わなければ、Safie APIは使用できない仕組みになっています。 Safie Developersを再度開き、「③-8 APIキー認証」を選択し、「APIキー認証アプリケーション一覧」の画面を開きました。「③-9 連携デバイス管理」を選択し、「APIキー認証アプリケーション>連携デバイス管理」の画面を開きました(図10)。 図10. Safie Developers APIキー認証アプリケーション一覧(アプリケーション作成後) - 2 「APIキー認証アプリケーション>連携デバイス管理」の画面では、アプリケーションコードを入力したデバイスの情報が表示されました。「③-10 設定」より「認証状況」を「承認済み」に変更しました(図11)。 図10. Safie Developers APIキー認証アプリケーション一覧(アプリケーション作成後) - 2 これにてSafie APIを使うための準備が全て整いました。 ④Safie APIでデバイス一覧を取得する アクセス可能なデバイス一覧を取得します。1台のみの登録なので、1台の情報のみ取得されます。デバイス一覧取得は特にパラメータ指定はないので、以下URLとAPIキーの設定のみを行います。 https://openapi.safie.link/v2/devices APIキーはAuthorizationのタブを選択し、Typeは「API Key」を選択しました。「Key」は「Safie-API-Key」の文字列を記入しました。「Value」は工程②で記録したAPIキーの文字列を記入しました。「Add to」は「Header」を選択しました。実行するとデバイス一覧がResponseに表示されました。表示された一覧からdevice_idの文字列を記録しました。 図11. Postman デバイス一覧取得実行 ⑤Safie APIで画像を取得する デバイスを指定して、画像取得を行いました。以下のURLの[device_id]の部分に、工程④で記録したdevice_idの文字列を記入しました。 https://openapi.safie.link/v2/devices/[device_id]/image 工程④の操作同様、AuthorizationにAPIキーを設定しました。実行するとResponseにカメラの画像が表示されました。 図12. Postman 画像取得実行 最後に 前回の投稿と同様の流れでしたが、Safie API v2(正式版)を実際に動かしてみた工程を紹介させて頂きました。Safie Developersのマニュアルに記載された内容ではあるのですが、今回の紹介を通して少しでも使い心地が伝わると嬉しく思います。 前回の紹介ではβ版は積極的に紹介できない旨を記載していましたが、正式版となり制限なく案内ができるようになりました。今回の投稿を見て気になった方は、是非、ご利用を検討頂ければと思います。
アバター
こんばんは。データ分析基盤グループ所属の大室です。 セーフィーではデータドリブン経営を推進すべく、2021年頃からデータ分析基盤のシステム開発が始まり、2022年に専門の組織「データ分析基盤グループ」が立ち上がっています。 システムも組織も産声を上げたばかりのデータ分析基盤。本記事ではその歴史や背景、組織やアーキテクチャにスポットライトを当てたいと思います! そもそもデータ分析基盤とは? データレイク DWH ETL BIツール セーフィーのデータ分析基盤ヒストリー データ分析基盤グループについて 所属組織 所属メンバー 開発手法 セーフィーのデータ分析基盤アーキテクチャ 旧基盤 新基盤 最後に We are hiring! そもそもデータ分析基盤とは? データ分析基盤とは、「 大量のデータを収集・蓄積・整形し、分析を行うためのシステム基盤 」のことを指します。 データ分析基盤は様々な要素で構成されますが、代表的な構成要素として「 データレイク 」「 DWH 」「 ETL 」「 BIツール 」が挙げられます。 データレイク データレイク とは、大量のデータを保存・管理するためのアーキテクチャまたはシステムを指します。 構造化データや半構造化データ、非構造化データなど、あらゆる形式のデータをそのまま保存することができます。 DWH DWH とは、データウェアハウス( D ata W are H ouse)の略語です。 異なるデータソースからのデータを統合し、組織の意思決定に役立つ情報を提供するために設計されたデータベースシステムを指します。 データは通常、タイムスタンプや一定のルールに基づいて整理され、組織のビジネスプロセスや意思決定のニーズに合わせて構造化されます。 ETL ETL とは、( E xtract/ T ransform/ L oad)の頭文字をとった略語です。 DWHへデータを統合する際に用いられる各プロセスを指します。 ①Extract(抽出・収集) 様々なデータソースから必要なデータを抽出するプロセスです。 データベース、ウェブサービス、ファイル、外部システムなど多岐にわたるデータソースからデータを抽出します。 ②Transform(変換・整形) データを目的の形式や構造に変換するプロセスです。 データの変換には、クレンジング(不正確なデータの修正や欠損値の処理)・結合・集計などが含まれます。 ③Load(格納) 抽出・変換したデータをデータレイクやDWHに格納するプロセスです。 元々、 E xtract → T ransform → L oad の順に処理するフローが主流だったため、 ETL という略語が定着しました。 データの変換処理は専用のサーバー上で実行するのが一般的でしたが、近年(2010年代〜)は性能の高いクラウド型DWHが台頭したため、 E xtract → L oad → T ransform といったようにデータをDWHに格納してからDWH上で変換処理を実行するフロー、所謂 ELT という略語も使われ始めています。 BIツール BIツール (ビジネスインテリジェンスツール)とは、データを分析・可視化するためのソフトウェアツールを指します。 企業や組織がデータに基づいたビジネスの意思決定を効率的に行うための手段として用いられます。 BIツールは主に下記の機能を備えています。 ①データソースへの接続機能 DWH・Webサービス・Excelファイル・csvファイルなど、様々なデータソースに接続し、必要なデータを取得します。 ②データの分析機能 データの集計・集約・フィルタリング・グルーピングなどの分析操作を実行し、データからパターンや傾向を発見する手助けとなります。 ③可視化・ダッシュボード機能 データを視覚的に理解しやすい形式で表示します。グラフ・表・マップなどのコンポーネントを使用し、データをインタラクティブなダッシュボードに組み込むことができます。 BIツールそのものをデータ分析基盤に含めるかどうかは議論の余地があると思いますが、ビジネスユーザーがデータを分析・可視化する上で非常に便利なツールであることは間違いありません。 セーフィーのデータ分析基盤ヒストリー セーフィーのデータ分析基盤に纏わる歴史について紹介します。 ※脚色が若干入っています。 私は2022年4月にジョインしたので、データ分析基盤導入以前の状況はあまり詳しくないのですが… 巷の噂によると、当時は社内のデータ分析に関する問い合わせがCTOに集中し、多くの時間を割いている状況だったとのことです。 事業拡大&社員増加に伴いデータ分析の社内ニーズが日々高まる一方、当社のCTOは他にも重要な役割を担っています。 上記のような問い合わせ対応に追われ続けるのは、全社的にあまり好ましい状況ではなかったようです。 また、当時はサービスのバックエンドのデータベースシステムから直接データを参照していましたが、分析用の処理に特化したシステムではないため、分析要件を満たすデータの算出には多くの時間を要する状況でした。 このような状況の打開策として「データ分析基盤の構築」と「専門組織の組成」が挙がり、2021年下期からCTO直下組織「CTO室」主導でデータ分析基盤の構築が急ピッチで進められました。 並行してデータ人材(データエンジニアやデータアナリスト)の採用も積極的に行い、2022年上期にCTO室から「データ分析基盤の構築」及び「データ分析の支援」業務をスピンアウトした組織「 データ分析基盤グループ 」が立ち上がりました。 専門組織が立ち上がって一安心!………とはいかず、急ピッチで構築したデータ分析基盤は性能や拡張性に多数の課題を抱え、徐々に運用保守が困難な状況に陥ってしまいました。 この状況を打破すべく、2023年上期からデータ分析基盤リニューアルPJが始まり、今に至っています。 データ分析基盤グループについて 所属組織 セーフィーでは機能別組織を採用しており、社長直下組織を除くと、開発本部・企画本部・営業本部・カスタマーサービス本部・経営管理本部の5つの本部が存在します。 データ分析基盤グループは、 開発本部 配下の 第3開発部 に所属しています。 組織図 | セーフィー株式会社 - Safie Inc. 第3開発部には、顧客管理システムや契約管理システムなど、社内業務に必要不可欠なシステムの開発を担う組織「 業務システムグループ 」と、 Store People Detection Pack に代表される、映像解析AIの開発を担う組織「 AI Visionグループ 」も所属しています。 所属メンバー データ分析基盤グループには、データ分析基盤の開発や運用保守を担う データエンジニア と、各部署のデータ分析支援を担う データアナリスト が所属しています。 役割は異なりますが、ユーザーヒアリング・要件定義・テーブル設計など、密に連携して様々な業務に取り組んでいます。 開発手法 セーフィーでは開発手法として スクラム を導入している開発組織が多いのですが、データ分析基盤グループでも2023年1月から導入しています。 まだまだ試行錯誤の段階ではありますが、スクラムを導入したことによって、属人化の低減やコラボレーションの促進、柔軟性の向上や生産性の改善に繋がっていると感じています。 スクラムについては下記の記事に詳しく書かれていますので、ぜひご覧下さい! engineers.safie.link engineers.safie.link セーフィーのデータ分析基盤アーキテクチャ セーフィーが提供するクラウド録画サービスのバックエンド(通称:Safieプラットフォーム)はAWSの各サービスで構築されており、デバイス(カメラなど)やアプリケーション(Web/モバイル/AIなど)が発するデータのほぼ全てがAmazon AuroraやAmazon S3に格納されます。 セキュリティの都合上、Safieプラットフォーム側とデータ分析基盤側のAWSアカウントは分離しており、AWS Lake Formation経由でデータ分析に必要なデータのみ共有する仕組みを構築しています。 AWS Lake Formationを用いたデータ共有の詳細については下記の記事に詳しく書かれていますので、ぜひご覧下さい! zenn.dev 旧基盤 前述の通り、データ分析基盤は現在リニューアル中のため、通称「 旧基盤 」と「 新基盤 」2つのアーキテクチャが存在する状況です。2023年度内に新基盤への移行を完了する見込みです。 旧基盤では、AWS Lake Formationで共有されたデータをAWS GlueのPython Shell JobでAmazon S3にParquet形式で出力し、Amazon EC2上のShell Script/Python/SQLでAmazon Redshiftにデータを抽出/格納/変換する処理を実装しています。 社内のユーザーはRedashやTableauを通してAmazon Redshift上のデータを参照することができます。 新基盤 Amazon S3にデータを出力する処理までは旧基盤と同じですが、新基盤ではDWHをSnowflakeに変更し、Amazon S3 → Snowflakeへデータを転送する処理にはSnowpipe(Snowflakeの継続的なデータロードの仕組み)、データの変換処理にはdbt(data build tool)を採用しています。 社内のユーザーには、SQLでアドホックに分析したい場合 = Snowsight(Snowflake標準のWeb UI)、SQLが苦手な場合や視覚的に分析したい場合 = Tableau経由でSnowflake上のデータを参照していただく予定です。 旧基盤で採用していたRedashは、データ分析基盤以外の用途でも幅広く利用されている&別部署が管理しているといった事情を踏まえ、新基盤では採用しない方針です。 また、旧基盤では所謂データモデリング手法が特に採用されておらず、テーブルの構造や関係性が非常に把握し難い状況に陥っていたのですが、新基盤ではData Vault ModelingとDimensional Modelingを採用し、データモデリングの改善を図っています。 データモデリング手法については下記の記事に詳しく書かれていますので、ぜひご覧下さい! engineers.safie.link 最後に 今回はセーフィーにおけるデータ分析基盤の歴史や背景、組織やアーキテクチャについて紹介しました。 新基盤の全貌やリニューアルPJの取り組み内容については詳しく紹介できなかったため、また改めて別の記事で紹介したいと思います。 We are hiring! 余談ですが、データ分析基盤グループは慢性的な人手不足でして…一緒に働いてくれる仲間を大募集中です! セーフィーのデバイスやアプリケーションからは1日あたり数十億件のログが発生し、データ量は線形的に増加しています。 このような超ビッグデータを効率的に処理し、ビジネスの意思決定に繋げるための仕組みを一緒に作ってみませんか? ご興味のある方は セーフィー採用サイト を覗いてみて下さい。 皆様のご応募、心よりお待ちしております!
アバター
こんにちは。サーバサイドエンジニアの村田です。 今回は、セーフィーのエンジニアがどんな開発環境を使っているのかアンケートをとってみたので、その結果をみなさんに共有したいと思います。 セーフィーにはどんなエンジニアがいるの? アンケート結果 使用しているエディタ・IDEは? 使用しているキーボードは? 使用しているマウス・トラックパッドは? まとめ セーフィーにはどんなエンジニアがいるの? 今回はセーフィーの全エンジニアを対象にアンケートを取りました。セーフィーには様々な領域を担当するエンジニアが在籍しており、まとめると以下の職種のエンジニアがいます。 フロントエンドエンジニア サーバサイドエンジニア インフラエンジニア/SRE AI/画像処理エンジニア 組み込みソフトウェアエンジニア iOS/Android エンジニア 業務システムエンジニア QAエンジニア データエンジニア 各領域のエンジニアがどんな仕事をしているかは以下の記事で詳しく紹介しているので、こちらもぜひ読んでいただけると嬉しいです! engineers.safie.link アンケート結果 今回は開発環境として、使用しているエディタ・IDE、キーボード、マウスの3項目についてアンケートを取りました。それでは早速結果をみていきましょう! 使用しているエディタ・IDEは? VS Codeが半数を超える人気となっていました。選択理由としては「高機能で使いやすい」「プラグインが豊富」「Web開発では標準となっている」といった意見が挙げられていました。 続いて、モバイルアプリ開発で必須となっている Xcode, Android Studio が多く、次いでVim が一定の支持を集めました。Vimの選択理由としては「軽量」「高速で行の入れ替えが可能なため、思考の整理に使える」といった意見が挙げられていました。 少数派としては Intellij IDEAやPyCharmといったJetBrains系のIDEや、Emacs, Atom, Sublime Text, 秀丸エディタを使っている方もいました。 こちらの結果は、私自分の実感ともほぼ一致していました。普段ペアプログラミングなどで同僚がエディタを使っている様子を見る機会がありますが、やはりVS Codeを使っている人が多く、人気が高い印象を持っていました。 使用しているキーボードは? ノートPC純正のキーボードを使っている方が半分以上を占めている結果となりました。選択理由としては、「特に不便がない」「キーボードを持ち歩くのが面倒」といった意見が目立ちました。 それに次いで、 HHKBとRealforce が多くみられました。どちらのキーボードも「打鍵感が心地よい」「コンパクトさがよい」といった意見が多く挙げられていました。 少数派としては、Keychron、ELECOM スリムキーボード、ARCHISS ProgresTouch RETRO TKL、Mistel、また自作キーボードを使っているという方もいらっしゃいました。いずれのキーボードも打鍵感や見た目、キー配置、またトラックポイントの有無といったところが、こだわりポイントとして挙げられていました。 ノートPC純正のキーボードが一番多いというのは意外でした。エンジニアは結構 HHKBや自作キーボードなど、キーボードに高いこだわりを持っている方が多い印象がありましたが、セーフィーのエンジニアの中では少数派のようです。 使用しているマウス・トラックパッドは? ノートPCのトラックパッドが一番多く、続いて 会社支給のLogicoolのワイヤレスマウスを使っている方 が多い結果となりました。マウス・トラックパッドに関しては特にこだわりがないという意見が多数でした。 それ以外のものを使っている方の選択理由としては、トラックボールの有無や静音性、また腱鞘炎対策などの観点が挙げられていました。 まとめ いかがでしたでしょうか? 開発環境に関するアンケートの結果をご紹介しましたが、人それぞれ大事にしている観点が違うのが興味深いですね。今回はエディタ、キーボード、マウスの3つを対象に調査を行いましたが、他の項目についても調査してみると面白い結果が得られるかもしれません。ターミナルやシェル、リモートワークで使うようになったマイクなどもこだわりがある方が多いと思いますので、機会があればぜひ調査してみたいと思います。 最後まで読んでいただき、ありがとうございました!
アバター
企画部の居内です。Safie APIのプロジェクトの運用を担当しています。 Safie APIはβ版として提供しておりましたが、先ごろ正式版がリリースされ、現在多くのお客さまにご利用いただいております。 今回は、非エンジニアの方でも気軽にSafie APIを体験していただく機会を提供できればと思い、本職のエンジニアの方には簡単な内容ではありますが、Google App Script(GAS)を使ってSafie APIを利用する方法を、テックブログを通してご紹介できればと思っております。 この記事では、Google App Script(GAS)でSafie APIを実行し、指定したカメラの静止画をSlackへ投稿する方法をお伝えいたします。 大まかな流れ 実際の操作手順 1.Safie Developersにログインし、APIキーのアプリケーションを作成する 2.Safie Viewerにログイン後、今回APIを利用したいカメラに対しDeveloper連携を行う 3.Slack APIを開きbotを作成、botのトークンを取得する 4.Google App Scriptの新しいプロジェクトを作成する 5.Google App Scriptのプロジェクト内にプロパティを設定する 6.Google App Scriptにコードを記載し、実行を行う 最後に 大まかな流れ コードだけ知りたい方もいらっしゃるかと思いますので、先に記載します。 var API_KEY = PropertiesService.getScriptProperties().getProperty( 'API_KEY' ) var SLACK_TOKEN = PropertiesService.getScriptProperties().getProperty( 'SLACK_TOKEN' ) function postImageToSlack() { var safieApiUrl = 'https://openapi.safie.link/v2/devices/[device_id]/image' var params = { 'method' : 'GET' , 'content_type' : 'application/x-www-form-urlencoded' , 'headers' : { 'Safie-API-Key' : API_KEY, } , } var apiResponse = UrlFetchApp.fetch(safieApiUrl, params) Logger.log(apiResponse.getResponseCode()); var imageBlob = apiResponse.getBlob(); // ここからSlack投稿 var slackUrl = 'https://slack.com/api/files.upload' var payload = { 'channels' : '[channel名]' , 'file' : imageBlob, 'filename' : "image.jpg" } var options = { 'method' : 'post' , 'payload' : payload, 'content_type' : 'multipart/form-data' , 'headers' : { Authorization: "Bearer " + SLACK_TOKEN } } var slackResponce = UrlFetchApp.fetch(slackUrl,options) Logger.log(slackResponce) } ※[device_id]と[channel名]は必要なものを記載ください 実際の大まかな流れは以下の通りです。 Safie Developersにログインし、APIキーのアプリケーションを作成する Safie Viewerにログイン後、今回APIを利用したいカメラに対しDeveloper連携を行う Slack APIを開きbotを作成、botのトークンを取得する Google App Scriptに新しいプロジェクトを作成する Google App Scriptのプロジェクト内にプロパティを設定する Google App Scriptにコードを記載し、実行を行う 実際の操作手順 1.Safie Developersにログインし、APIキーのアプリケーションを作成する まず、カメラを契約中のメールアドレスでSafie Developersにログインしてください。 ログイン後、右上に表示される「管理ツール」をクリックし、管理ツール画面を開きます。 画面に表示された「APIキー認証アプリケーション新規作成」をクリックし、画面の表示に従い入力を進めてください。 今回、カメラを契約中のメールアドレスでログインしていますので、プランをトライアル、利用サービスはDevices v2を選択ください。 アプリケーション作成後は、画面の表示に従い、APIキー新規発行を実行ください。 2.Safie Viewerにログイン後、今回APIを利用したいカメラに対しDeveloper連携を行う カメラを契約したアカウントでSafie Viewerにログインすると、左メニューにユーザー設定の項目があるのでクリックください。表示された画面の「Developers連携」をクリックし、続けて「アプリケーションを追加」を押下、1の手順で作成したAPIキーを「アプリケーションコード」の欄に入力し、連携するカメラを選択ください。 連携後、Safie Developersに再度ログインを行い、1の手順で作成したアプリケーションの「連携デバイス管理」からこの作業でDevelopers連携申請を行ったカメラの承認作業を行ってください。 注意頂きたいこととして、Safie ViewerからDeveloper連携を行えるのはカメラを契約したアカウントでログインしたときのみです。シェアユーザーでは連携を行えません。 3.Slack APIを開きbotを作成、botのトークンを取得する 今回新たにSlackbotを作成する際は、下記を参考に作成ください。 Create New App > From scratchを選択し、アプリ名と利用するワークスペースを選択する Permissions > Bot Token Scope > files:writeで権限を許可する Install to Workspaceで今回利用するワークスペースにアプリをインストールする Permissions内のBot User OAuth Tokenをコピーする カメラの静止画を送付したいSlackチャンネルに、この手順で作成したSlackアプリをインストールする 4.Google App Scriptの新しいプロジェクトを作成する Google App Scriptの画面を開いて、左上のボタンから新しいプロジェクトを作成します 5.Google App Scriptのプロジェクト内にプロパティを設定する 左メニューのプロジェクトの設定を選択し、スクリプト プロパティに下記を作成ください。 プロパティ 値 API_KEY 手順1で取得したAPIキー SLACK_TOKEN 手順3で取得したBot User OAuth Token 6.Google App Scriptにコードを記載し、実行を行う エディタに戻り、下記を実行ください。実行すると手順2で連携したカメラの情報が表示されるので、表示されたdevice_id=の後ろに記載された文字列をコピーしてください。 var API_KEY = PropertiesService.getScriptProperties().getProperty( 'API_KEY' ) function mydevices() { var safieApiUrl = 'https://openapi.safie.link/v2/devices' var params = { 'method' : 'GET' , 'content_type' : 'application/x-www-form-urlencoded' , 'headers' : { 'Safie-API-Key' : API_KEY, } , } var response = UrlFetchApp.fetch(safieApiUrl, params) var json = JSON.parse(response) Logger.log(json.list); } エディタの内容を下記に書き換え、[device_id]の箇所に先ほどコピーしたdevice_idを、[channel名]の箇所に、手順3でインストールを行ったチャンネル名を記載ください。 実行すると、指定したSlackチャンネルにカメラの静止画が投稿されます。 var API_KEY = PropertiesService.getScriptProperties().getProperty( 'API_KEY' ) var SLACK_TOKEN = PropertiesService.getScriptProperties().getProperty( 'SLACK_TOKEN' ) function postImageToSlack() { var safieApiUrl = 'https://openapi.safie.link/v2/devices/[device_id]/image' var params = { 'method' : 'GET' , 'content_type' : 'application/x-www-form-urlencoded' , 'headers' : { 'Safie-API-Key' : API_KEY, } , } var apiResponse = UrlFetchApp.fetch(safieApiUrl, params) Logger.log(apiResponse.getResponseCode()); var imageBlob = apiResponse.getBlob(); // ここからSlack投稿 var slackUrl = 'https://slack.com/api/files.upload' var payload = { 'channels' : '[channel名]' , 'file' : imageBlob, 'filename' : "image.jpg" } var options = { 'method' : 'post' , 'payload' : payload, 'content_type' : 'multipart/form-data' , 'headers' : { Authorization: "Bearer " + SLACK_TOKEN } } var slackResponce = UrlFetchApp.fetch(slackUrl,options) Logger.log(slackResponce) } 最後に Google App Scriptはトリガーから、時間指定でscriptの実行が可能ですので、毎週何曜日に実行などの定期実行が可能になります。 例えば弊社であれば、郵便や宅配便が届いた際には部署ごとのラックに置いておりますので、宅配便などの受付スペースからラックまでを画角に収めれば、Slackに投稿される画像だけで配送物が届いているかの確認も可能です。 いかがでしたでしょうか。 単純な静止画の連携だけでも、総務の方から、店舗の管理、また映像の共有は行わない従業員間の連携などが可能になるものだと思っています。防犯用途以外でも、APIによりカメラの利用用途が広がるかと思いますのでぜひご利用いただければ幸いです。
アバター
こんにちは。サーバサイドエンジニアの村田です。 2023年7月14日に開催された freeeさん主催の技術イベント「freee Tech Night」に参加してきましたので、ご報告します! freee-tech-night.connpass.com freee Tech Night とは 当日の様子 感想 freee Tech Night とは 今回参加させていただいた「freee Tech Night」というイベントは freee株式会社さん主催の技術イベントで、freeeさんがクラウド会計・人事労務ソフトを開発する中で得られた経験や成果を共有する場として、定期的に開催されているイベントになっています。 他の会社のエンジニアを招いての開催も多く、今回は機会をいただきまして、弊社と一緒に開催させていただく運びとなりました。 freee-tech-night.connpass.com 当日の様子 freeeさんとセーフィーそれぞれ2名ずつエンジニアが参加し、パネルディスカッション形式で進行していきました。セーフィーからは自分と同じくサーバサイドのエンジニアでテックリードでもある 鈴木敦志 さんが参加しました。 イベントはオフライン開催でしたが、YouTube liveで配信もされておりオンラインでの参加もできるようになっていました。本格的なスタジオ設備がオフィス内にあってびっくりしました。 今回のイベントのテーマは「巨大なデータとの向き合い方」ということで、 freeeさんとセーフィーの双方で、どういったデータを扱っているか、データの規模、巨大なデータを扱う上での苦労や工夫などをお互いに話していきました。 セーフィー側からは、まず扱うデータとしては映像データが常時入ってきており、大量にあること。また映像データだけでなく解析データも非常に量が多い点についてお話させていただきました。 実際のデータ量についても具体的にご紹介させていただきました。セーフィーでは1日に約900TBの映像データが入ってきており、それが契約プランに応じた期間(7日 ~ 1年) 保存され、トータルで約23PB程度の映像データがあること、また解析データは一日約1億件が溜まっていっています。 これらの巨大なデータを扱う上で工夫している点として、まずインフラ側の構成についてご紹介させていただきました。セーフィーでは一定カメラ台数(約13000台程度)ごとにそれを処理するためのインフラ構成(ネットワーク、DB、redis、サーバ類)一式を新たに用意する水平分割の構成をとっており、それによりインフラ側の負荷を抑えられるようにしています。 また、DBクエリ周りの工夫点としては、MySQLは削除が遅いため論理削除 + 非同期バッチで削除するパターンをよく使っている点などについてご紹介させていただきました。 freeeさんの方からは、会計データを大量に扱っていることや確定申告や決算申告のタイミングで負荷高くなること、対策として地道なクエリチューニングや読み込み系のクエリをreaderに向けるようにすることによるwriterの負荷軽減の取り組みなどをについて、ご紹介いただきました。 短い時間ではありましたが、お互いに実際に扱っているデータの性質や規模感、取り組みについてかなり具体的に共有することができました。 他にもドメインのキャッチアップ方法など、いろんなお話をしたのですが、ここには書ききれないので、詳しい内容はyoutube live配信のアーカイブを見てみてください! www.youtube.com 感想 こういったイベントに出演するのは初めてだったのでとにかく緊張しましたが、とても学びある時間になり、良い経験となりました。終了後の懇親会では、いろんな方とお話できたのも楽しく、オフラインイベントにはこういった人と直接会話する楽しさがあるのが良いなと感じました。 イベントの運営について、freeeのエンジニアの方にお話を伺ったところ、もともと現場のエンジニア呼びかけから始まったイベントで、運営も自分達でやられているとのことで、自分達自身で技術発信の文化を作っていっているところがとても素晴らしいなと感じました。 またこういうイベントがあれば積極的に参加したいと思います!
アバター
こんにちは。Safieでモバイルアプリの開発をしている渡部です。 モバイル版Safie Viewerでは、バージョン3.11.0から、新しいバージョンがリリースされるとユーザーに向けてアップデートを知らせるアラートが表示されるようになっています。 iOS版 Safie Viewer 今回は、このアップデート機能の実装について振り返ってまとめてみました。 実装の経緯 必要な要件 サービス選定 実装 RemoteConfigの設定 アプリ側の設定 効果 実装の経緯 モバイル版Safie Viewerでは現在約1ヶ月おきに定期リリースを行っています。以前は開発サイクルがもう少し長期間だったのですが、チーム体制が充実したこともありコンスタントに新しいバージョンを用意することができるようになりました。 それに伴い、ユーザーに新バージョンをアピールする必要性もより高まりました。新機能を実装したり、バグを修正するスピードが早まったものの、ユーザーに気づいてもらえない限りは意味がありません。 より安定したバージョンを使ってもらうためにも、アップデート機能を実装することになりました。 必要な要件 実装にあたって、まずはモバイルアプリのアップデート機能で求める要件を洗い出しました。 立ち上げたアプリが対象バージョンより低い場合、アップデートのお知らせをアラートで表示したい 対象バージョンとは必ずしも最新版ではなく、任意のバージョンを指定したい。 強制と半強制を使い分けたい アプリを立ち上げてアップデートについてアラートが表示された際に、アップデートしないと先に進めないパターン(強制)と、アップデートせずともアプリを使い続けられるパターン(半強制)の2つを、バージョンによって切り替えたい。 深刻なバグ修正の場合は強制で、それ以外の場合は半強制でお知らせしたい。 その他、いろいろな設定値を持ちたい アラート表示を開始/終了する日時、アラート表示の対象となるOSバージョンなど。 iOS版・Android版の設定を一括で管理したい 手軽に更新したい サービス選定 アップデート機能を実現するための仕組みはすでに色々なサービスで提供されています。 例えば、 ①iTunes Search APIを使用する(iOSのみ) ②Google Play Core Libraryを使用する(Androidのみ) ③アップデート機能用の3rdパーティライブラリを使用する ④バックエンドサービスにアップデートの設定ファイルを置き、アプリ側で読み込む など。 ①や②は公式のサービスですが、カスタム性は乏しいです。③もメンテナンスのことを考えるとできれば避けたくなります。 ということで、今回は、前述した要件を満たすために④の方法で、サービスはFirebase Remote Configを活用することにしました。 ④を実現するだけなら、その他のBaaSを活用するやり方もありますが、 すでにFirebaseを導入済みだったこと コンソール上のUIのみで複数ファイルを更新できる手軽さ 以上を加味して、 Remote Configを選びました。 実装 Firebaseプロジェクトの作成については、今回は割愛します。 RemoteConfigの設定 要件をふまえて、コンソールで以下のようなjsonを設定しました。 { "parameters": { "ios_update": { "defaultValue": { "value": { "version":"3.19.0", "is_force":true, "start_date":"2023-02-28", "support_os_version":"13.0" } }, "valueType": "JSON" }, "android_update": { ... } } } version: 対象バージョン。この数値より低いバージョンのアプリを起動したら、アップデート案内のアラートを表示する。 is_force: 強制か半強制か start_date:アラート表示開始日 end_date: アラート表示終了日 support_os_version:アプリのサポート対象OS アプリ側の設定 プロジェクトにRemote ConfigのSDKを追加 iOS CocoaPodsを使用しているので、Podfileに以下を追加。 pod 'Firebase/RemoteConfig' Android build.gradleに以下を追加。 implementation 'com.google.firebase:firebase-config-ktx:21.4.0' 2. アップデートのアラートを表示したいタイミングで、RemoteConfigからjsonを取得するように実装。 iOS アプリを起動して最初に立ち上がるViewControllerにて override func viewWillAppear (_ animated : Bool ) { // ここで取得 } 通知をタップして任意の画面を開いた時 func userNotificationCenter (_ center : UNUserNotificationCenter , didReceive response : UNNotificationResponse , withCompletionHandler completionHandler : @escaping () -> Void ) { // ここで取得 } フォアグラウンドからの復帰時 func applicationWillEnterForeground (_ application : UIApplication ) { // ここで取得 } 取得の流れ // シングルトンオブジェクトを作成 remoteConfig = RemoteConfig.remoteConfig() // RemoteConfigから値を取得 remoteConfig.fetch() { (status, error) -> Void in remoteConfig.activate { changed, error in // RemoteConfigStruct:RemoteConfigで設定したjsonと対応するStruct let response = try ? JSONDecoder().decode(RemoteConfigStruct. self , from : remoteConfig [“ios_update”].dataValue) } } Android 起動時、フォアグラウンド復帰時にて class MainApp : Application() { override fun onCreate() { ProcessLifecycleOwner. get ().lifecycle.addObserver( object : DefaultLifecycleObserver { override fun onStart(owner: LifecycleOwner) { // ここで取得 } }) } } 取得の流れ // シングルトンオブジェクトを作成 val remoteConfig = Firebase.remoteConfig // RemoteConfigから値を取得 remoteConfig.fetchAndActivate().addOnCompleteListener { task -> if (task.isSuccessful) { val gson = GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create() // RemoteConfigData:RemoteConfigで設定しているjsonと対応するDataクラス val remoteConfigData= gson.fromJson(remoteConfig.getString(”android_update”), RemoteConfigData :: class .java) } } 3. 取得した設定値(remoteConfigData)によって、アラートの表示/非表示、強制/半強制を出し分けるように実装。 効果 強制アップデートの仕組みを実装してから、ユーザーは以前よりも速やかに最新版に移行してくれるようになったのか、Analyticsから確認してみました。 いずれも、新バージョンをリリースしてから1ヶ月間のユーザー推移を表しています。 ①強制アップデート実装前(旧バージョン3.9 → 新バージョンv3.10) ②実装して2ヶ月経過(旧バージョン3.12 → 新バージョンv3.13) ③実装して半年以上経過(旧バージョン3.18 → 新バージョンv3.19) 強制アップデート実装から時間が経過するにつれ、旧バージョン使用数の減退が早まっています。旧バージョンの使用割合も減りました。 今まで以上に多くのユーザーがより早く新バージョンを使用することに繋がり、アプリの安定化に繋がったかなと思います🙌
アバター
​データ分析基盤グループでデータエンジニアをしている平川です。 DataVaultに関する記事の第2回目となります。(第1回の記事は こちら です) 第2回の記事は、DataVaultモデリングの中心となるHub/Link/Satelliteをdbtのパッケージを利用して作っていくという内容です。 2,3回目の内容が当初と少し変わっていますので、再掲いたします。 第1回: DataVaultってなに?どんな特徴があるの? 第2回: automate_dvを使ってDataVaultモデリングの中心となるテーブルを作ってみてわかったこと ← 今回はここ 第3回: BusinessVault、発展的なSatelliteテーブルやキーがNullだった場合の対処方法についてなど 前回のおさらい はじめに automate_dvについて automate_dvとは? 便利な点は何? 注意点 automate_dvの使い方 インストール方法 Hub/Link/Satelliteの実装 ハッシュキーの生成 Hubの生成 Linkの生成 Satelliteの生成 automate_dvを使う際の小技 automate_dvを使用していてハマったポイント 履歴化されているデータソースを取り込む場合 取り込みたいモデルの構造 対象のモデルのデータの変化 automate_dvを使ってSatelliteテーブルを作った際の結果 RawVault層までの実装にautomate_dvを使った感想など まとめと次回予告 参考資料 前回のおさらい 前回の記事では、データウェアハウス設計における1つのアプローチであるDataVaultの特徴やメリットについて説明しました。DataVaultは、柔軟性や拡張性に優れ、大量のデータを効果的に管理することができます。 また、DataVaultを実装するためには、Hub/Link/Satelliteという構造を持ったテーブルを生成する必要があります。これらのテーブルの生成には手間がかかることがありますが、automate_dvというパッケージを使うことでテーブル生成を簡易にすることができます。 今回の記事では、このテーブルの生成をサポートするautomate_dvの紹介と実際のテーブル生成までの手順を解説します。 はじめに 第2弾の記事では、データウェアハウスのテーブル構築方法について、automate_dv 1 を利用した手法について紹介します。 まずは、前回の内容を振り返りつつ、データウェアハウスの設計に関する用語を簡単に説明します。 用語 意味 ELT データウェアハウスにおけるデータの取り込み方法の1つで、データを抽出してから変換し、最後にロードする手法です dbt データウェアハウスの構築や管理をするためのオープンソースのツールです。SQL(一部Jinja)でデータパイプラインを記述できます。 DataVault データウェアハウスの設計パターンの1つで、拡張性や柔軟性の高さなどが特徴です。 Hub DataVaultで構築する上での中心となるテーブルで、ビジネスキーとそのハッシュキーで構成されます。 Link DataVaultでのHub同士の関係性を表すテーブルで、関連するHubのハッシュキーと関連する2つのHubのビジネスキーをconcatしてハッシュ化したキーで構成されます。 Satellite HubやLinkのキーに対して付随する属性情報を保持するテーブルです。属性情報をまとめてハッシュ化したHashdiffカラムによって、変化を検知することができます。 次のセクションでは、automate_dvを使用してDataVaultのテーブル構築を行う方法について、手順を解説します。 automate_dvについて automate_dvとは? dbtのパッケージの1つで、DataVault2.0モデルに基づいたデータウェアハウスの構築をサポートしてくれます。このパッケージのマクロを利用することで、DataVaultモデリングに関するテーブルのSQL実装を簡単に行うことができます。 便利な点は何? automate_dvを利用することで、Hub/Link/Satelliteテーブルやハッシュ化したキーを含むテーブルの作成を容易にすることができます。 これにより、SQLの記述量を大幅に削減し、テーブルの実装にかかる時間を短縮することができます。 注意点 automate_dvは便利なツールですが、利用しているプラットフォームによっては使用できないマクロがあるため注意が必要です。 例えば、一部のマクロはRedshiftやPostgreSQLでは利用できません。一方で、Snowflake/BigQuery/MS SQL Serverでは、現在一般提供されているマクロを使用することができます。 プラットフォームによって利用できる機能に違いがあるため、使用する前にドキュメントを参照することをおすすめします。 automate_dvの使い方 インストール方法 automate_dvを使うにはdbtを実行する環境にインストールする必要があります。dbtのプロジェクト直下にある。 package.yml に以下のようにautomate_dvパッケージの依存関係を追加し、 dpt deps を実行してください。 packages : - package : Datavault-UK/automate_dv version : 0.9.5 Hub/Link/Satelliteの実装 dbt run or dbt build を実行することで作成したSQLから各種ビューとテーブルを作成してくれます。 Hub/Link/Satelliteを作成するための各SQLの書き方を以下で説明していきます。 ハッシュキーの生成 Hub/Link/Satelliteを実装するにはそれぞれハッシュ化されたキーとhashdiffが必要になります。 そのため、実装する3テーブルが参照するビューも必要になるため、まずはハッシュキーを含むビューをautomate_dvを使って実装していきます。 実装例として、 orders , customers , products , customers_orders , orders_products という5つのソーステーブルからハッシュキーを含むビューを作成していきます。 hashed_ordersのSQLは下記のようになります。(hashed_orders以外のSQLについては折り畳んでありますので詳細を知りたい方は展開していただければと思います。) {%- set yaml_metadata -%} -- ① source_model: -- ② orders derived_columns: RECORD_SOURCE: " !ORDER_MANAGEMENT " hashed_columns: ORDER_HASH_DIFF: is_hashdiff: true columns: - ORDER_NAME - ORDER_AMOUNT - CREATED_AT ORDER_HK: - ORDER_ID {%- endset -%} {% set metadata_dict = fromyaml(yaml_metadata) %} {{ automate_dv.stage( include_source_columns= true , source_model=metadata_dict[ " source_model " ], derived_columns=metadata_dict[ " derived_columns " ], hashed_columns=metadata_dict[ " hashed_columns " ], null_columns=none, ranked_columns=none, ) }} -- ③ ① : jinjaの記述方法でyaml形式で各種パラメータを設定する ② : ハッシュキーの設定や参照するモデルの設定などをする ③ : automate_dvのstageマクロを使い各種パラメータを引数に設定する その他のモデルのクエリ hashed_products {%- set yaml_metadata -%} source_model: products derived_columns: RECORD_SOURCE: "!ORDER_MANAGEMENT" hashed_columns: HASH_DIFF: is_hashdiff: true columns: - PRODUCT_NAME - PRICE - CREATED_AT PRODUCT_HK: - PRODUCT_ID {%- endset -%} {% set metadata_dict = fromyaml(yaml_metadata) %} {{ automate_dv.stage( include_source_columns=true, source_model=metadata_dict["source_model"], derived_columns=metadata_dict["derived_columns"], hashed_columns=metadata_dict["hashed_columns"], null_columns=none, ranked_columns=none, ) }} hashed_customers {%- set yaml_metadata -%} source_model: customers derived_columns: RECORD_SOURCE: "!ORDER_MANAGEMENT" hashed_columns: HASH_DIFF: is_hashdiff: true columns: - EMAIL - PREFECTURE - CREATED_AT CUSTOMER_HK: - CUSTOMER_ID {%- endset -%} {% set metadata_dict = fromyaml(yaml_metadata) %} {{ automate_dv.stage( include_source_columns=true, source_model=metadata_dict["source_model"], derived_columns=metadata_dict["derived_columns"], hashed_columns=metadata_dict["hashed_columns"], null_columns=none, ranked_columns=none, ) }} hashed_customers_orders {%- set yaml_metadata -%} source_model: customers_orders derived_columns: RECORD_SOURCE: "!ORDER_MANAGEMENT" hashed_columns: HASH_DIFF: is_hashdiff: true columns: - CREATED_AT CUSTOMER_HK: - CUSTOMER_ID ORDER_HK: - ORDER_ID CUSTOMER_ORDER_HK: - CUSTOMER_ID - ORDER_ID {%- endset -%} {% set metadata_dict = fromyaml(yaml_metadata) %} {{ automate_dv.stage( include_source_columns=true, source_model=metadata_dict["source_model"], derived_columns=metadata_dict["derived_columns"], hashed_columns=metadata_dict["hashed_columns"], null_columns=none, ranked_columns=none, ) }} hashed_orders_products {%- set yaml_metadata -%} source_model: orders_products derived_columns: RECORD_SOURCE: "!ORDER_MANAGEMENT" hashed_columns: HASH_DIFF: is_hashdiff: true columns: - CREATED_AT ORDER_HK: - ORDER_ID PRODUCT_HK: - PRODUCT_ID ORDER_PRODUCT_HK: - ORDER_ID - PRODUCT_ID {%- endset -%} {% set metadata_dict = fromyaml(yaml_metadata) %} {{ automate_dv.stage( include_source_columns=true, source_model=metadata_dict["source_model"], derived_columns=metadata_dict["derived_columns"], hashed_columns=metadata_dict["hashed_columns"], null_columns=none, ranked_columns=none, ) }} 続いて、今作成したハッシュキー含むモデルから、Hub/Link/Satelliteを作成していきます。 Hubの生成 ordersのHubテーブルの作成は以下のSQLのようになります。(customersとproductsのHubテーブルは折りたたみ内にSQLを記載しています) {{ config(materialized= " incremental " ) }} {%- set yaml_metadata -%} source_model: hashed_orders src_pk: ORDER_HK src_nk: ORDER_ID src_ldts: LOADED_AT src_source: RECORD_SOURCE {%- endset -%} {% set metadata_dict = fromyaml(yaml_metadata) %} {{ automate_dv.hub( src_pk=metadata_dict[ " src_pk " ], src_nk=metadata_dict[ " src_nk " ], src_ldts=metadata_dict[ " src_ldts " ], src_source=metadata_dict[ " src_source " ], source_model=metadata_dict[ " source_model " ], ) }} その他のモデルのクエリ hub_products {{ config(materialized="incremental") }} {%- set yaml_metadata -%} source_model: hashed_products src_pk: PRODUCT_HK src_nk: PRODUCT_ID src_ldts: LOADED_AT src_source: RECORD_SOURCE {%- endset -%} {% set metadata_dict = fromyaml(yaml_metadata) %} {{ automate_dv.hub( src_pk=metadata_dict["src_pk"], src_nk=metadata_dict["src_nk"], src_ldts=metadata_dict["src_ldts"], src_source=metadata_dict["src_source"], source_model=metadata_dict["source_model"], ) }} hub_customers {{ config(materialized="incremental") }} {%- set yaml_metadata -%} source_model: hashed_customers src_pk: CUSTOMER_HK src_nk: CUSTOMER_ID src_ldts: LOADED_AT src_source: RECORD_SOURCE {%- endset -%} {% set metadata_dict = fromyaml(yaml_metadata) %} {{ automate_dv.hub( src_pk=metadata_dict["src_pk"], src_nk=metadata_dict["src_nk"], src_ldts=metadata_dict["src_ldts"], src_source=metadata_dict["src_source"], source_model=metadata_dict["source_model"], ) }} Linkの生成 orderとproductの関係性を記述するLinkテーブルの作成は以下のSQLのようになります。(link_customers_ordersは折りたたみ内にSQLを記載しています。) {{ config(materialized= " incremental " ) }} {%- set yaml_metadata -%} source_model: hashed_orders_products src_pk: ORDER_PRODUCT_HK src_fk: - ORDER_HK - PRODUCT_HK src_ldts: LOADED_AT src_source: RECORD_SOURCE {%- endset -%} {% set metadata_dict = fromyaml(yaml_metadata) %} {{ automate_dv. link ( src_pk=metadata_dict[ " src_pk " ], src_fk=metadata_dict[ " src_fk " ], src_ldts=metadata_dict[ " src_ldts " ], src_source=metadata_dict[ " src_source " ], source_model=metadata_dict[ " source_model " ], ) }} その他のモデルのクエリ link_customers_orders {{ config(materialized="incremental") }} {%- set yaml_metadata -%} source_model: hashed_customers_orders src_pk: CUSTOMER_ORDER_HK src_fk: - CUSTOMER_HK - ORDER_HK src_ldts: LOADED_AT src_source: RECORD_SOURCE {%- endset -%} {% set metadata_dict = fromyaml(yaml_metadata) %} {{ automate_dv.link( src_pk=metadata_dict["src_pk"], src_fk=metadata_dict["src_fk"], src_ldts=metadata_dict["src_ldts"], src_source=metadata_dict["src_source"], source_model=metadata_dict["source_model"], ) }} Satelliteの生成 orderの属性情報を記述するSatelliteテーブルの作成は以下のSQLのようになります。(sat_customersとsat_productsは折りたたみ内にSQLを記載しています。) {{ config(materialized= ' incremental ' ) }} {%- set yaml_metadata -%} source_model: hashed_orders src_pk: ORDER_HK src_hashdiff: HASH_DIFF src_payload: - ORDER_NAME - ORDER_AMOUNT - CREATED_AT src_ldts: LOADED_AT src_source: RECORD_SOURCE {%- endset -%} {% set metadata_dict = fromyaml(yaml_metadata) %} {{ automate_dv.sat( src_pk=metadata_dict[ " src_pk " ], src_hashdiff=metadata_dict[ " src_hashdiff " ], src_payload=metadata_dict[ " src_payload " ], src_ldts=metadata_dict[ " src_ldts " ], src_source=metadata_dict[ " src_source " ], source_model=metadata_dict[ " source_model " ], ) }} その他のモデルのクエリ sat_customers {{ config(materialized='incremental') }} {%- set yaml_metadata -%} source_model: hashed_customers src_pk: CUSTOMER_HK src_hashdiff: HASH_DIFF src_payload: - EMAIL - PREFECTURE - CREATED_AT src_ldts: LOADED_AT src_source: RECORD_SOURCE {%- endset -%} {% set metadata_dict = fromyaml(yaml_metadata) %} {{ automate_dv.sat( src_pk=metadata_dict["src_pk"], src_hashdiff=metadata_dict["src_hashdiff"], src_payload=metadata_dict["src_payload"], src_ldts=metadata_dict["src_ldts"], src_source=metadata_dict["src_source"], source_model=metadata_dict["source_model"], ) }} sat_products {{ config(materialized='incremental') }} {%- set yaml_metadata -%} source_model: hashed_products src_pk: PRODUCT_HK src_hashdiff: HASH_DIFFF src_payload: - PRODUCT_NAME - PRICE - CREATED_AT src_ldts: LOADED_AT src_source: RECORD_SOURCE {%- endset -%} {% set metadata_dict = fromyaml(yaml_metadata) %} {{ automate_dv.sat( src_pk=metadata_dict["src_pk"], src_hashdiff=metadata_dict["src_hashdiff"], src_payload=metadata_dict["src_payload"], src_ldts=metadata_dict["src_ldts"], src_source=metadata_dict["src_source"], source_model=metadata_dict["source_model"], ) }} automate_dvを使う際の小技 automate_dvのドキュメントでは、stageマクロを実行する際のsource_modelには最新分だけを含んだモデルを対象にするのがベストプラクティスとされています。 ですが、止むを得ず参照するモデルに複数日付が含まれていることがあるかもしれません。この場合、最新分だけが存在する層を作るということもできますが、stageマクロを使いつつ、最新分だけのビューを作成することも可能です。 具体的な手順としては、 automate_dv.stage 部分をwith句に入れて一時テーブルとして定義し、日付を絞る方法です。以下のように記述することで実現できます -- パラメータの設定は省略 with stage as ( {{ automate_dv.stage( include_source_columns= true , source_model=metadata_dict[ " source_model " ], derived_columns=metadata_dict[ " derived_columns " ], hashed_columns=metadata_dict[ " hashed_columns " ], null_columns=none, ranked_columns=none, ) }} ) select * from stage where loaded_at = ' YYYY-MM-DD ' automate_dvを使用していてハマったポイント automate_dvは、データウェアハウスの構築において高いパフォーマンスを発揮するツールですが、日本語の情報も少なくハマってしまった時に原因を理解するのに時間がかかると思います。ここでは、実際にDataVault層のSatelliteテーブルを構築する際にハマったポイントについて説明していきます。 履歴化されているデータソースを取り込む場合 Satelliteテーブルに取り込む対象が、前日からの増分のみであれば、automate_dvのSatelliteマクロを使用しても問題ありません。しかし、Satelliteが参照するモデルが過去から現在までの全期間のレコードを持っている場合、予期しない結果になる可能性があります。具体的なサンプルと動作を見ていきましょう。 取り込みたいモデルの構造 カラム名 history_id customer_id amount created_at loaded_at 対象のモデルのデータの変化 2023-04-02 history_id customer_id amount created_at loaded_at 1 1 1000 2023-04-01 12:00:00 2023-04-02 00:00:00 2 2 1500 2023-04-01 13:00:00 2023-04-02 00:00:00 2023-04-03 history_id customer_id amount created_at loaded_at 1 1 1000 2023-04-01 12:00:00 2023-04-03 00:00:00 2 2 1500 2023-04-01 13:00:00 2023-04-03 00:00:00 3 1 1000 2023-04-02 12:00:00 2023-04-03 00:00:00 4 1 3000 2023-04-02 12:30:00 2023-04-03 00:00:00 2023-04-04 history_id customer_id amount created_at loaded_at 1 1 1000 2023-04-01 12:00:00 2023-04-04 00:00:00 2 2 1500 2023-04-01 13:00:00 2023-04-04 00:00:00 3 1 1000 2023-04-02 12:00:00 2023-04-04 00:00:00 4 1 3000 2023-04-02 12:30:00 2023-04-04 00:00:00 5 3 5000 2023-04-03 15:00:00 2023-04-04 00:00:00 6 1 1000 2023-04-03 16:00:00 2023-04-04 00:00:00 automate_dvを使ってSatelliteテーブルを作った際の結果 SatelliteテーブルのSelect結果 上記のSatelliteテーブルの赤い枠で囲まれた1行目と5行目は同一のレコードになっています。全期間の履歴を保持するようなモデルに対してSatelliteマクロを使うとこのような現象が起きてしまいます。 このような状態になってしまう原因は、automate_dvのマクロを使用することで生成されるSQLを見ることでわかります。 以下生成されたSQLを抜粋 WITH source_data AS ( SELECT a.CUSTOMER_HK, a.HASH_DIFF, a.AMOUNT, a.CREATED_AT, a.LOADED_AT, a.RECORD_SOURCE FROM hs_order_histories AS a WHERE a.CUSTOMER_HK IS NOT NULL ), latest_records AS ( SELECT a.CUSTOMER_HK, a.HASH_DIFF, a.LOADED_AT FROM ( SELECT current_records.CUSTOMER_HK, current_records.HASH_DIFF, current_records.LOADED_AT, RANK () OVER ( PARTITION BY current_records.CUSTOMER_HK ORDER BY current_records.LOADED_AT DESC ) AS rank FROM sat_order_histories AS current_records JOIN ( SELECT DISTINCT source_data.CUSTOMER_HK FROM source_data ) AS source_records ON current_records.CUSTOMER_HK = source_records.CUSTOMER_HK ) AS a WHERE a. rank = 1 ), records_to_insert AS ( SELECT DISTINCT stage.CUSTOMER_HK, stage.HASH_DIFF, stage.AMOUNT, stage.CREATED_AT, stage.LOADED_AT, stage.RECORD_SOURCE FROM source_data AS stage LEFT JOIN latest_records ON latest_records.CUSTOMER_HK = stage.CUSTOMER_HK AND latest_records.HASH_DIFF = stage.HASH_DIFF WHERE latest_records.HASH_DIFF IS NULL ) SELECT * FROM records_to_insert このSQLでは、ソースモデル(ハッシュキーを含むモデル)とSatelliteテーブルから条件を付与して抽出したテーブルを結合して、新たに追加するレコードを作成しています。 with句の2番目に定義されている最新レコード(latest_records)を見るとRankを使用して最新のレコードを抽出しています。 Satelliteテーブルに追加されるレコードは、ソースデータと最新レコードを結合し、ソースデータにだけ存在するレコードを抽出しています。 (where句での latest_records.HASH_DIFF IS NULL でソースデータにだけ存在するレコードを取ろうとしています。) 今回例として取り上げたような、同じハッシュキーに対して、異なるHASH_DIFFが追加されるようなテーブルの場合、Satelliteマクロは予期した動作をしないことがあります。 対応策としては、最初に述べたように、Satelliteテーブルが参照するモデルには最新分しか含まないようにすることです。中間層に手を加えたくない場合は、SQLでSatelliteテーブルに存在しないレコードのみを抽出するようなSQLを書きます。 select distinct customer_hk, hash_diff, amount, created_at, loaded_at, record_source from hs_order_history as stg where customer_hk is not null {% if is_incremental() %} and not exists ( select 1 from ( select customer_hk, hash_diff from {{ this }} as cur where stg.customer_hk = cur.customer_hk and stg.hash_diff = cur.hash_diff ) ) {% endif %} RawVault層までの実装にautomate_dvを使った感想など 1つのビジネスキーに対して、ログを溜め続けるようなデータソースがある場合、Satelliteマクロを使用する際に生成されるSQLを見ると、余計なレコードが生成されてしまうことがあります。そのため、データソースによってはマクロの使用の有無や参照モデルの修正など、使い分けや手を加える必要があり、少し面倒に感じました。 マクロを使用することで、キーのハッシュ化など、SQLで記述する場合には多くのコード量が必要な部分をマクロで置き換えられるのはすごく便利に感じました。 予期しない動作が発生した場合、コンパイルされたSQLを見ることで原因を理解することはできますが、automate_dvの実装を見るとマクロ内でマクロを呼び出しているためautomate_dvでの仕組みを理解するのには時間がかかる印象でした。 データソース側で物理削除されている場合、Satelliteではレコードが削除されたかどうかが判断できないので、StatusTrackingSatelliteが欲しくなる場面がありましたが、現在(2023年6月時点)はautomate_dvにマクロはまだ実装されていないのでSQLを直接書く必要がありました。(EffectiveSatelliteのマクロはあるのでもしかしたらそちらで対応できるかもしれません) まとめと次回予告 今回の記事ではDataVaultモデリングの中心となるHub/Link/Satelliteを生成するためのdbtのパッケージの1つであるautomate_dvについて紹介させていただきました。 automate_dvはSQLだけで、Hub/Link/Satelliteの生成クエリを実装するのに比べて、非常に少ない記述量で各種テーブルの実装を行うことができます。 当たり前ですが、マクロによって生成されるSQLがどんなことをしているのかを理解するのが、使いこなす上で重要になってくると思います。 次回は、BusinessVaultや今回の記事で名前だけ出てきているEffectiveSatelliteやStatusTrackingSatelliteの紹介や、ビジネスキーがNullだった場合の対応方法などについて紹介させていただければと思います。 参考資料 automate_dvドキュメントページ 記事を書き始めた時はdbtvaultでしたが、気づいたらautomate_dvに変わってました... ↩
アバター
はじめに アプリケーションの構成 Electron Vue.js TypeScript 新規アプリケーションのハンズオン 事前準備 Vue CLIの導入 Vueプロジェクトの作成 Electronの組み込み アプリケーションの起動 preloadスクリプトの作成 メインプロセスへのipcMainモジュールの追加 preloadスクリプトの作成 住所検索画面の作成 終わりに はじめに こんにちは、開発本部第2開発部、Webフロント第2グループでフロントエンド開発をしている伊原です。 私が開発を担当している Safie Entrance2 にて、認証に用いる顔認証端末のセットアップができるアプリケーションが欲しい、という要望が上がりました。 そこでVue3とElectronを利用して簡素なアプリケーションを作成しましたので、本記事ではVue3+Electronのアプリケーション構築方法のハンズオンを記載します。 当時は状態管理に Pine を利用していましたが、今回のセットアップでは状態管理まではせず、アプリケーションの基本的な部分までをセットアップします。 アプリケーションの構成 本記事執筆時点での情報をもとに記載しているため、フレームワークのアップデートによりセットアップが異なる可能性があります。その場合は各技術で紹介している公式ドキュメントをご確認ください。 アプリケーションに採用した各技術について簡単に説明します。 Electron 次の説明は公式サイトより引用です。 Electron は、JavaScript、HTML、CSS によるデスクトップアプリケーションを構築するフレームワークです。 Electron は Chromium と Node.js をバイナリに組み込むことで、単一の JavaScript コードベースを維持しつつ、ネイテイブ開発経験無しでも Windows、macOS、Linux で動作するクロスプラットフォームアプリを作成できます。 https://www.electronjs.org/ja/docs/latest Electronを利用することで、Webフロントエンドで利用されている技術をそのまま利用してデスクトップアプリケーションを作成する事ができます。 今回はVueフレームワーク+TypeScriptを利用します。 Electronの特徴として、以下2つのプロセスで動作しており、それぞれ役割が異なっています。 メインプロセス  アプリケーション全体の挙動の制御をする。Node.jsのAPIを利用できる。 レンダラープロセス  ウェブコンテンツをレンダリングする。 この2つのプロセスはお互いに独立しており、レンダラープロセスからNode.jsのAPIを直接利用してローカルにログを出力したりする事はできないようになっています。 これはセキュリティ上の理由からで、Webサイトの画面上からローカルのあらゆるものが操作可能、というのは問題がありますよね。 また、レンダラープロセスでAPIを呼び出す場合、API提供側がCORSを有効にしている場合、CORSエラーが発生します。 これを回避するため、メインプロセス側でAPI呼び出し後、結果をレンダラープロセスに渡してあげる必要があります。 メインプロセスでレンダラープロセスの通信結果を受け取りたい場合には、プロセス間通信と呼ばれる方式によってデータをやりとりします。 詳細は割愛しますが、後ほどのハンズオンで説明するpreloadスクリプトを利用して通信します。 各プロセスの詳細な説明は次の公式サイトをご確認ください。 https://www.electronjs.org/ja/docs/latest/tutorial/process-model https://www.electronjs.org/ja/docs/latest/tutorial/tutorial-preload Vue.js Web ユーザーインターフェース構築のための、親しみやすく、パフォーマンスと汎用性の高いフレームワークです。 今回は、公開されている最新バージョンの Vue3 を利用します。 TypeScript 有名な型付け可能なaltJSです。 Electron、 VueともにTypeScriptをサポートしているため、JavaScriptではなくTypeScriptを利用します。 新規アプリケーションのハンズオン それでは、これまで紹介した技術を使ってアプリケーションの雛形を作ってみたいと思います。 事前準備 事前に以下の環境を構築しておきます。 Node.js: v16.20 npm: 8.19.4 Electron+Vueの開発では、セットアップを簡単にするVue CLI Plugin Electron Builderというプラグインが提供されています。そちらを利用してVue Cliにてセットアップを進めていきます。 Vue CLIの導入 アプリケーションのセットアップのため、Vue-CLIを導入します。 Vue CLI Plugin Electron Builderでは執筆時点では4が推奨されていますので、4.Xの最新版であるv4.5.19をインストールします。 $ npm install @vue/cli@4.5.19 Vueプロジェクトの作成 Vue-CLIでプロジェクトのテンプレートを作成します。 $ vue create my-electron-prj 作成時に表示されるオプション選択は、初めのvue3-tsを選択します。 Electronの組み込み Vue CLI Plugin Electron Builderを利用してelectronをプロダクトに組み込みます。 先ほど作成したプロジェクトのディレクトリに移動し、electron-builderを追加します。 $ cd my-electron-prj $ vue add electron-builder 途中の質問には以下のように回答します。 ここで1つ問題があり、インストールされるElectron13.0.0はEOLとなっているため、Node.16に対応したサポート期間中のElectronにアップデートします $ npm install electron@22.0.0 このままビルドするとvue-loaderのエラーで怒られるので、v16をインストールします。 $ npm install vue-loader-v16 アプリケーションの起動 ここまで環境構築が終わったら、実際にアプリケーションを立ち上げてみましょう。 $ npm run electron:serve 以下のような画面でアプリケーションが起動すれば成功です。 ここまででアプリケーションの大枠は作成できました。ひとまずお疲れ様でした。 アプリケーションの雛形は作成できましたが、現状ではメインプロセスとレンダラープロセス間で通信するための仕組みがまだ実装されておらず、メインプロセスでの処理結果を画面側に表示する、というような事はできません。 次以降でメインプロセスとレンダラープロセスを通信させるためのpreloadスクリプトを作成していきます。 preloadスクリプトの作成 メインプロセスとレンダラープロセスのプロセス間通信のため、preloadスクリプトを作成します。 今回は試しに、公開されている 住所検索API を利用して取得した結果を画面上に表示してみようと思います。 メインプロセスへのipcMainモジュールの追加 src/background.tsにメインプロセスからレンラダープロセスへ非同期で通信するためのモジュールであるipcMainモジュールをelectronからインポートします。 import { app , protocol , BrowserWindow , ipcMain } from "electron" ; インポートと共に、住所取得APIの処理を追加します。 追加前に、APIリクエストのためにHTTPSリクエストライブラリとしてaxiosをインストールしておきます。 $ npm install axios zipcode(郵便番号)を引数として住所情報を取得する処理を追加します。 ipcMain.handle ( "searchZipcode" , async ( _event: IpcMainInvokeEvent , zipcode: string ) => { const response = await axios. get( "https://zipcloud.ibsnet.co.jp/api/search" , { params: { zipcode } , } ); return response.data ; } ); preloadスクリプトの作成 レンダラープロセスからメインプロセスで追加したAPIを呼び出し、結果を受け取るためにipcRender, contextBridgeをインポートします。 ipcRenderはメインプロセスの指定のチャネルにイベント・引数を送信し、処理結果を受け取ります。contextBridgeはwindowにAPIを注入し、window[apiKey]にてAPIにアクセスできるようになります。 以下、preload.tsのファイルです。 import { contextBridge , ipcRenderer } from "electron" ; contextBridge.exposeInMainWorld ( "api" , { searchZipcode: ( zipcode: string ) => { return ipcRenderer.invoke ( "searchZipcode" , zipcode ); } , } ); pathをインポートし、作成したpreload.tsのパスをbackground.tsのcreateWindow()関数のBrtowserWindow()をnewしているところのwebPreferencesに追加します。 import path from "path" ; webPreferences: { nodeIntegration: process .env .ELECTRON_NODE_INTEGRATION as unknown as boolean , contextIsolation: ! process .env.ELECTRON_NODE_INTEGRATION , preload: path.join ( __dirname , "preload.js" ), } , これでpreloadスクリプトの追加はOK…と言いたいところですが、このままだと1つ問題があり、ビルド時にpreload.jsがバンドルされません。 対策は次で紹介されている通りで、ルートディレクトリにvue.config.jsを作成し、preloadスクリプトのパスを記述します。 vue-cli-plugin-electron-builder/docs/guide/guide.md at master · nklayman/vue-cli-plugin-electron-builder · GitHub module . exports = { pluginOptions: { electronBuilder: { preload: "src/preload.ts" , } , } , } ; 住所検索画面の作成 これでAPIを呼び出す準備はできたので、住所検索画面を作成していきます。 簡単な動作確認が目的なので、テンプレートを作成した際のviews/About.vueを差し替えて作成してしまいます。 < template > < div class= "search-zipcode" > < div > < input v-model = "zipcode" / > < button :disabled = "isFetching" @click = "onSearchZipcode" > 検索 < /button > < /div > < div > < span v- if= "isFetching" > 取得中 < /span > < span v- else> 住所情報: {{ result }} < /span > < /div > < /div > < /template > < script lang = "ts" > import { defineComponent , ref } from "vue" ; export default defineComponent ( { name: "SearchZipcode" , setup () { const zipcode = ref ( "" ); const result = ref ( "" ); const isFetching = ref ( false ); const onSearchZipcode = async () => { try { isFetching.value = true ; const response: { message: string | null ; results: [ { address1: string ; address2: string ; address3: string ; kana1: string ; kana2: string ; kana3: string ; prefcode: string ; zipcode: string ; } ] ; } = await ( window as any ) .api.searchZipcode ( "4340016" ); const { address1 , address2 , address3 } = response.results [ 0 ] ; result.value = address1 + address2 + address3 ; } catch ( error ) { console .log ( error ); } finally { isFetching.value = false ; } } ; return { onSearchZipcode , zipcode , result , isFetching } ; } , } ); < /script > 郵便番号の入力フォームと検索ボタン、それと検索結果表示用の欄のみがあるシンプルなページです。windowインターフェースに追加されたsearchZipcodeに入力した郵便番号を渡し、受け取った結果を結合して住所情報として表示します。 試しに、セーフィー株式会社の本社郵便番号「141-0033」を入力して検索してみます。 住所情報に想定通りの情報が表示されています。これで、メインプロセスのAPI呼び出しとプロセス間通信が問題なく動作している事が確認できました。 ここまでのハンズオンでアプリケーションの雛形作成、API呼び出しからのプロセス間通信が実現できました。後は同様に作っていただければ、Electronのアプリケーションが作成できるはずです。 終わりに 本記事ではVue3 + TypeScript + Electronでのアプリケーションの開発方法の基本をご説明しました。皆さんも本記事のハンズオンを活用して、Electronのアプリケーションを作ってみてください!
アバター
こんにちは、セーフィーでエンジニアをしております伊林です。 今回は、Safieでリモートワークツールとして利用している Gather. Town とその活用方法を紹介していきます。 Gather. Townとは 導入のきっかけ SafieでのGatherの利用方法 常時接続して適宜相談する 緊急対策室の導入 休憩所を設ける   その他、導入して良かったこと 別チームの人の様子がわかるようになった、交流する機会ができた オフィスワークとリモートワークを似た感覚でできるようになった まとめ Gather. Townとは Gatherの様子   Gatherは、リモートオフィスツールの一種で、カスタマイズ可能な2次元のMap上でリモートメンバーと会話やチャットを行うことができます。 Safieでは、数年前からエンジニア組織内で活用しています。いくらか知見がたまってきたこともあり、紹介します。 導入のきっかけ 私は以前、Safieの機械学習エンジニアとして働いていたのですが、数年前にサーバーサイドエンジニアのチームへ異動を行いました。 当時は機械学習のアルゴリズム開発や、実地評価用の小規模なシステム開発などを担当していました。ただ、作成したアルゴリズムを製品に落とし込むのためには課題も多く、特にサービス開発の経験がたりないと感じたため、一度サーバーサイドエンジニアとしてサービス開発の経験を積みたいと思ったことが理由です。   異動したのはちょうどコロナの全盛期であり、多くの人がリモートで働いていました。 これにより、以下のような問題を感じることがありました。 リモートだと気楽に通話しづらい(気がする) フルリモートのメンバーと交流する機会がなくなる(気がする) 簡単な内容の相談であれば、チャットによる相談で済む場合が多いですが、相談内容が複雑な場合など、音声で説明した方が効率的な場合があると思います。 特にチームへ入りたての状況では、プロジェクトやチームに関する背景知識が欠けていることからコミュニケーションミスが発生しやすく、通話でやり取りした方が良い場合が多いと思います。 しかし、リモートの場合、相手の状態が把握しづらいこともあり、オフラインのように気楽に通話をお願いしづらく、つい二の足を踏んでしまうということがありました。 また、フルリモートだと昼食を一緒に食べるような機会は減るので、オフラインと比較すると交流の機会が減ってしまうと思います。   一方、フルリモートで働くことに対する課題がないか聞き込みを行ったところ、以下のような課題が見つかりました。 トラブルが起きた際に気づかない、気づくのに遅れる、特定のメンバーだけで情報共有されてしまう場合がある オフィスで働いている場合、”なにか”(サーバに問題が発生するなどの緊急事態)が起きている場合に、周囲の雰囲気で気付けることがありますが、フルリモートの場合はSlackのスレッドを確認する必要がありました。これにより気づくのが遅れたり、そもそもチャットを見逃してしまうというようなこともあったようです。   以上のような問題意識から、自チーム内でGatherを試験的に導入してみたことがきっかけでGatherを使い始めました。最近では、他チームでも利用してみたいという声が多かったこともあり、部署全体で利用しています。 SafieでのGatherの利用方法 常時接続して適宜相談する Google Meetのように通話が必要な場合にのみ接続するのではなく、基本的に業務中は常時Gatherに接続するようにしています。 また、各自ステータスを設定することで、話しかけていいかどうかを表明することにしています。これにより、相手に話しかけていいかどうか迷うことを少なくしています。 話しかけていい状態 話しかけて欲しくない状態 緊急対策室の導入 「トラブルが起きた際に気づかない、気づくのに遅れる、特定のメンバーだけで情報共有されてしまう場合がある」を解決するために導入しました。   障害対応を行うための部屋をGatherのMAP上に設けており、障害対応を行う際はここに集まって対応するようにしています。これにより、Gatherの画面を見れば何かの障害に対応していることが一目でわかるようになりました。   完全に音声通話のみで障害対応を行うと、作業履歴が残らなくて後で困るなどの問題もあるので、基本的にSlackによるテキスト投稿と併用しています。が、トラブルに気づく速度は導入前よりも改善された気がしています。 緊急対策室 休憩所を設ける 「フルリモートのメンバーと交流する機会がなくなる(気がする)」を解決するために、導入しました。 フルリモートの場合、休憩時間を一人で過ごすことが多く、職場の人と話す機会は減る傾向にあると思います。 そこで、MAP上に明示的に休憩所を設けることにしました。仕事の相談や雑談など、気が向いた時に誰かと話す場所として利用しています。   エンジニアの勉強会 以前から勉強会は開催されており、エンジニアの技術交流の場として利用されていました。 Safieには、AI、サーバー、フロント、モバイル、デバイスなど、さまざまなエンジニアが在籍しており、各チームで日々、さまざまな勉強会が開かれています。 これ自体はとてもいい取り組みだと思っているのですが、同じチームの人のみが参加しているものが多い印象で、別チームの勉強会であっても気軽に参加できるようにしたいと思っていました。 Gatherで勉強会を開催することで、あまり明るくない分野の勉強会でも気軽に参加できるようになるのではないかと思っています。   その他、導入して良かったこと 別チームの人の様子がわかるようになった、交流する機会ができた 導入前は、フルリモートの場合、自分が所属するチーム *1 以外の様子が見えづらいということがありました。 Gatherを導入してからは、ある程度誰と誰が会話している、会議しているなど別チームの動きがなんとなくわかるようになりました。   オフィスワークとリモートワークを似た感覚でできるようになった Gatherを導入する前は、 オフィスは、 会社の人と交流したり、複雑内容の相談をする場所 リモートワークは、仕事を家に持ち帰って作業すること という感覚でした。 Gatherを導入してからは、リモートで働く場合でもオフラインとある程度似たような感覚で仕事できるようになりました。   まとめ 今回は、SafieにおけるGatherの活用方法を紹介しました。ツール活用の参考なれば嬉しいです。     ja.gather.town *1 : Safieでは、主に5人程度の規模のチームで開発を行うことが多いです。
アバター