- 概要
- ユーザを準備
- デプロイ用のIAMポリシー設定
- デプロイ実行
- staging環境を作成する
- アプリケーションの削除
- まとめ
概要
テクノロジー戦略本部の長兵衛です。
以前の記事でRuby on Jetsのローカル環境を作成するところまで書きました。
今回は実際にデプロイするところまでまとめてみたいと思います。
続きを読むテクノロジー戦略本部の長兵衛です。
以前の記事でRuby on Jetsのローカル環境を作成するところまで書きました。
今回は実際にデプロイするところまでまとめてみたいと思います。
続きを読むこんにちは。データサイエンス部の九町(くちょー)です。
今回は”サイエンス”とは関係ないのですが、会社のマクロ業績報告を自動化してみたお話をしたいと思います。ハードルとなったのは、弊社で敷いているセキュリティです。というか、この仕組みがないと逆にセキュリティ低過ぎて、別問題が発生してしまうのですが..。
弊社のデータサイエンスチームの立ち位置としては、分析から新しい示唆を出すことはもちろんですが、事業会社ならではのこのような集計レポーティングも大事な業務となっています。
きっかけは役員の方が毎朝送信されていた業務報告にありました。機密上、お見せ出来ないのが残念なのですが(本当に見て欲しかった..)、毎日送っているとは思えないほどの膨大な情報量で…。これは見る方も送る方も大変だろうなって誰もが直感的に思うものでした。
僕の当初のミッションとしては、これを本当に必要なKPIのみに精査して、そしてその報告を自動化することです(後で少し要件は増えてしまいますが。ここは次のセクションで記します)。ということで、ものすごく分かりやすくシンプルな課題という訳です。
どんな環境でも簡単に見れる….!?
実は基本要件まではほぼ1日で完了していましたが、それに対するFBでいただいたのがこの追加要件でした。
なので、この記事はこの追加要件に関わるお話になります。
これだけだと、何のことやら?という感じなので詳しく触れます。
シンプルに、もっと気軽に見たい!という要望でした。具体的には下記のようなことです。
まぁ確かにレポーティングする立場としては、見てもらえないと話にならない訳でして。。
言われたことはご尤もと理解。…とは言え、セキュリティ大丈夫、、、??
結論、要望を受け、目指したのは
というもの。
そう、「当初考えていた」ものとそれほど変わっておらず、「CWがLINEになっただけ」です。
….だけ、です。だけ、なんですが、諸々のハードルもありここからが少し大変でした。
LINE Notifyでは、PDFを投稿出来ない(png, imgなら可)
あれ?そうなのか。じゃあ画像にするしかないな..。と、思ったら、
GASでは、GSSをイメージ(png, img)化出来ない
え!?そんなことある?という落とし穴でした。ちなみに、
使用しているBIツールでもPDFは吐けるが、画像はダメ
でした。じゃあ、PDFをGoogle Driveに格納して、リンクを投げるしかないな。と、思ったら
(上にも書いたように)G Suiteは環境的にログインが面倒
あ、戻ってきてしまった。。これはボツ。
どこかサーバーに置こう。だけど、オープンなリンクでいいのか?誰かが端末落としたらまずいよな。
今更だけど、気軽に見たい⇔セキュリティ確保。この両輪ハードル高いな。
完全にど壺にハマりました..。
セキュリティにやっぱり不安は残るものの、過去分が見れないようにしておくって対応さえできれば情報としては限定できるし、ってことで、ここに落ち着きました。
ここまでがとても長かった。ようやく実装です。
GSSからPDFを作成し、Google Driveに格納
function createPDF(){ // PDFを格納するフォルダを指定 var folderId = "xxxxx"; var folder = DriveApp.getFolderById(folderId); // PDF化するスプレッドシートidを指定 var ssId = "xxxxx"; // PDF化するシートidを指定 var sheetId = "xxxxx"; // 作成するPDFのファイル名を指定(ここでは、「BST_RU_SalesReport_日時.pdf」となるようにしている) var fileName = "report"; var datetime = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyyMMdd_HHmmss'); var fullFileName = fileName + '_' + datetime + '.pdf'; // スプレッドシートをPDFにエクスポートするためのURL var url = "https://docs.google.com/spreadsheets/d/SSID/export?".replace("SSID", ssId); // PDF作成のオプションを指定 var opts = { exportFormat: "pdf", // ファイル形式の指定 pdf / csv / xls / xlsx format: "pdf", // ファイル形式の指定 pdf / csv / xls / xlsx size: "A4", // 用紙サイズの指定 legal / letter / A4 portrait: "true", // true → 縦向き、false → 横向き fitw: "true", // 幅を用紙に合わせるか sheetnames: "false", // シート名をPDF上部に表示するか printtitle: "false", // スプレッドシート名をPDF上部に表示するか pagenumbers: "false", // ページ番号の有無 gridlines: "false", // グリッドラインの表示有無 fzr: "false", // 固定行の表示有無 gid: sheetId // シートIDを指定 sheetIdは引数で取得 }; var url_ext = []; // 上記のoptsのオプション名と値を「=」で繋げて配列url_extに格納 for(optName in opts){ url_ext.push(optName + "=" + opts[optName]); } // url_extの各要素を「&」で繋げる var options = url_ext.join("&"); // API使用のためのOAuth認証 var gas_token = ScriptApp.getOAuthToken(); // PDF作成 var response = UrlFetchApp.fetch(url + options, {headers: {'Authorization': 'Bearer ' + gas_token}}); var blob = response.getBlob().setName(fullFileName); // PDFを指定したフォルダに作成 var fileId = folder.createFile(blob).getId(); }
無事想定通りのファイル名で指定のフォルダに格納されました。
それでは、このPDFをGCSにアップしていきます。
下準備として、まずはGCSのコンソール画面でPDFをアップする箱(プロジェクト、バケット、フォルダ)を用意します。今回は「RU Report」というプロジェクトの中に「bst_ru_reporting」というバケットを作成し、さらにフォルダとして「dailyReport_pdf」を準備しました。
それでは、アップしていきます。
function gcsUploadPFD(){ // バケット名の指定 var bucket = "bst_ru_reporting"; // アップロード先フォルダを指定 var gcsFolder = "dailyReport_pdf"; // 格納パス設定 var gcsPath = gcsFolder + "/" + fullFileName; // Access Tokenを取得 var service = checkOAuth(); // アップロードURLを組み立て var gcs_LongUrl = "https://www.googleapis.com/upload/storage/v1/b/" + bucket + "/o?uploadType=media&name=" + encodeURIComponent(gcsPath); // GSCにアップ var res = UrlFetchApp.fetch( gcs_LongUrl, { method:"POST", contentLength: bytes.length, contentType: blob.getContentType(), payload:bytes, headers: { Authorization: "Bearer " + service.getAccessToken() } } ); }
この工程はLINEに投稿するってことで、そのままのURLの長さだと見た目的に鬱陶しさがあるので、短縮にしたかっただけ(これはただのエゴですので、飛ばしてもOK)です。今回はbitly.comを利用しました(登録して、トークンを取得する必要があります)。ここの詳しい使い方はHashikakeさんに記載がありましたので参考にされてください。ちょっとした操作で簡単に短縮URLを作成することが出来ました。オススメです。
bitly.com
hashikake.jp
GAS側の記載はこんな感じ
function createBitlyUrl(longUrl) { var endpoint = 'https://api-ssl.bitly.com/v3/shorten?access_token=' + bitly_token + '&longUrl=' + longUrl; var result = UrlFetchApp.fetch(endpoint, { method: "GET", contentType: "application/json;" }); var json = JSON.parse(result.getContentText('utf-8')); return json.data.url; }
実装部分としては最終工程になります。あとは短縮したURLをLINE Notifyを使って該当のグループにえいっ!と投げます。これはLINEのアカウントがあれば、誰でも利用できるようです。詳しい内容はAPIドキュメントに記載があります。
notify-bot.line.me
ここでは2つの手順が必要です。
ログインしたら、アカウント名>マイページ>「トークンを発行する」で進みます
通知を投げたいグループを探して選択してください(赤枠)
トークン名(青枠)はここで設定されたものが実際の通知の文頭に使われます。これは変更は出来ないので注意してください。例えば、このトークン名を「BST営業報告」とすると、下記のような通知になります。
そうすると、下記のようにトークンが発行されます。画面にも記載がありますが、これは二度と表示されないので、メモを取ってください。
上記で選択したトークルーム(グループ)にLINE Notifyを追加してください。普通に誰かをグループに招待する手順と同様です。このLINE Notifyというユーザが投稿した感じになるので、グループに入っていないと投稿自体が出来ません。
LINEの設定は完了です。GASはこんな感じです。
function sendLINE(content) { var options = { "method": "post", "payload" : {"message": content}, "headers": {"Authorization": "Bearer " + GroupLINE_token} }; UrlFetchApp.fetch("https://notify-api.line.me/api/notify", options); }
contentは送信したい文章。GroupLINE_tokenは先ほど取得したトークンになります。
GAS全文です。(非エンジニアの為、可読性の低さや冗長性はご容赦ください..)
const GroupLINE_token = ['xxxxx']; //グループLINEのトークン:経営層グループ const bitly_token = 'xxxxx'; //bitlyのトークン const gssUrl = 'https://bit.ly/xxxxx'; //このGSSの短縮URL function createPDF(){ // PDFを格納するフォルダを指定 var folderId = "xxxxx"; var folder = DriveApp.getFolderById(folderId); // PDF化するスプレッドシートidを指定 var ssId = "xxxxx"; // PDF化するシートidを指定 var sheetId = "xxxxx"; // 作成するPDFのファイル名を指定(ここでは、「BST_RU_SalesReport_日時.pdf」となるようにしている) var fileName = "BST_RU_SalesReport"; var datetime = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyyMMdd_HHmmss'); var fullFileName = fileName + '_' + datetime + '.pdf'; // スプレッドシートをPDFにエクスポートするためのURL var url = "https://docs.google.com/spreadsheets/d/SSID/export?".replace("SSID", ssId); // PDF作成のオプションを指定 var opts = { exportFormat: "pdf", // ファイル形式の指定 pdf / csv / xls / xlsx format: "pdf", // ファイル形式の指定 pdf / csv / xls / xlsx size: "A4", // 用紙サイズの指定 legal / letter / A4 portrait: "true", // true → 縦向き、false → 横向き fitw: "true", // 幅を用紙に合わせるか sheetnames: "false", // シート名をPDF上部に表示するか printtitle: "false", // スプレッドシート名をPDF上部に表示するか pagenumbers: "false", // ページ番号の有無 gridlines: "false", // グリッドラインの表示有無 fzr: "false", // 固定行の表示有無 gid: sheetId // シートIDを指定 sheetIdは引数で取得 }; var url_ext = []; // 上記のoptsのオプション名と値を「=」で繋げて配列url_extに格納 for(optName in opts){ url_ext.push(optName + "=" + opts[optName]); } // url_extの各要素を「&」で繋げる var options = url_ext.join("&"); // API使用のためのOAuth認証 var gas_token = ScriptApp.getOAuthToken(); // PDF作成 var response = UrlFetchApp.fetch(url + options, {headers: {'Authorization': 'Bearer ' + gas_token}}); var blob = response.getBlob().setName(fullFileName); // PDFを指定したフォルダに作成 var fileId = folder.createFile(blob).getId(); // PDFをGCSにアップ var bytes = blob.getBytes(); var gcsUrl = gcsUploadPFD(fullFileName, bytes, blob); createContent(fileId, gcsUrl); } function createContent(fileId, gcsUrl) { var date = Utilities.formatDate(new Date(), 'JST', 'MM月dd日'); var yesterDay = sheet.getRange('A2').getValue(); var date2 = Utilities.formatDate(yesterDay, 'JST', 'MM月dd日'); var date2Month = Utilities.formatDate(yesterDay, 'JST', 'MM月'); var folderPath = "https://drive.google.com/file/d/"; var urlOpt = "?openExternalBrowser=1"; //LINEブラウザではなく、外部ブラウザで開かせる var PDF_longUrl = folderPath + fileId + urlOpt; var PDF_shoterUrl = createBitlyUrl(PDF_longUrl); //bitlyApiで短縮URLに var content = date; content += "\n\n▼" + date2 + "の実績速報"; // ~~~中略:GSSより各種実績を取得 content += "\n\n▼" + date2Month + "の着地予想"; // ~~~中略:GSSより各種実績を取得 content += "\n\n▼詳細はPDFで\n"; content += gcsUrl; content += "\n\n▼さらに詳細はGSSで\n"; content += gssUrl; sendLINE(content); } function sendLINE(content) { var options = { "method": "post", "payload" : {"message": content}, "headers": {"Authorization": "Bearer " + GroupLINE_token} }; UrlFetchApp.fetch("https://notify-api.line.me/api/notify", options); } function createBitlyUrl(longUrl) { var endpoint = 'https://api-ssl.bitly.com/v3/shorten?access_token=' + bitly_token + '&longUrl=' + longUrl; var result = UrlFetchApp.fetch(endpoint, { method: "GET", contentType: "application/json;" }); var json = JSON.parse(result.getContentText('utf-8')); return json.data.url; } // HTML側でチョイスしたファイルのIDを元に処理を開始する function gcsUploadPFD(fullFileName, bytes, blob){ // バケット名の指定 var bucket = "bst_ru_reporting"; // アップロード先フォルダを指定 var gcsFolder = "dailyReport_pdf"; // 格納パス設定 var gcsPath = gcsFolder + "/" + fullFileName; // Access Tokenを取得 var service = checkOAuth(); // アップロードURLを組み立て var gcs_LongUrl = "https://www.googleapis.com/upload/storage/v1/b/" + bucket + "/o?uploadType=media&name=" + encodeURIComponent(gcsPath); // GCSにアップ var res = UrlFetchApp.fetch( gcs_LongUrl, { method:"POST", contentLength: bytes.length, contentType: blob.getContentType(), payload:bytes, headers: { Authorization: "Bearer " + service.getAccessToken() } } ); // アクセスURLを短縮に var gcs_PublishUrl = "https://storage.googleapis.com/" + bucket + "/" + gcsPath; return createBitlyUrl(gcs_PublishUrl); }
忘れないようにしっかりアップされたPDFが削除されるように設定しておきます。
具体的には「ライフサイクル」という機能を使用していきます。このライフサイクルはバケット毎に設定が可能になっているようです。
GCSコンソールから該当のバケットに移動します。バケットに入ると、「ライフサイクル」タブ→ルールの追加と進みます。
今回は
期間以外にも月末に削除などの日時指定、Strageの料金をしっかり抑える為にがある一定になったらと言ったようなトリガーをつけることも可能です。
今回は次の日には見れなくなって良いので「1日」と設定しました。
クリックやアクセスをカウントした訳ではないので、定量評価はないのですが、ちょっと数字が違ったり(何故かGASトリガーが上手く動かず、数字が更新されないことがある)すると、すぐに連絡がきたりするので、見てくれてはいるんだなぁという程度の感覚はありますw
僕自身も強制的に業績を目にするのでその日やその週、月のマクロの業績は常に頭に入っているようになりました。これよりも細かい粒度にしてしまうと、情報量が多く、見なくなったりするので今くらいの情報量(昨日の実績、当月の着地予想、月間・週間推移[PDF])くらいで十分かなと思っており、満足しています。
今回は最初の要件からまさかのFB(気軽に見たい)を受けたことで、自分自身色々と勉強になりました。bitlyもLINE NotifyもGCSもGAS以外の部分は完全に初めてだったので。
分析レポートも大切ですが、こんな風に普段見れる数値をどれだけ社員に浸透・意識させられるかってことも業績改善に間接的に繋がると信じて、また違う角度で取り組めたらと思いました。
バイセルでは、このように事業に寄り添ったレポーティング、分析・開発を一緒にやってくれるメンバーを募集しています。
テクノロジー戦略本部の吉村です。
普段の業務では社内基幹システムのGYROの開発を主に行っています。
この記事ではBigqueryとGoogle Apps Script(以下GAS)を使った例をご紹介させていただければと思います。
業務でGYROの開発を通して実際にシステムを利用している方々から、
「この機能がどれぐらい、どのような内容で行われたか教えてほしい。」
というような依頼をいただくことがあります。
弊社ではBigqueryを利用していて、このようなrequestの分析を行うときなどにとても重宝しています。
ただBigqueryで取得したrequestのデータを一つ一つ数えていくようでは時間的にも精神的にも厳しいのでGAS(Google Apps Script)を利用してすきなフォーマットに整えて出力して共有するようにしています。
この記事では実際に上記のようなユースケースでどのようにデータを取得して、出力しているのかをGoogleが提供してくれている一般公開のデータをもとにご紹介させていただきます。
続きを読むテクノロジー戦略本部の一ノ瀬です。
モノをキャッシュにできるサービス『CASH(キャッシュ)』のiOSアプリの開発に携わっています。
今回は、CASHのiOSアプリを動かしているライブラリはどんなものを使っているか紹介したいと思います。
モバイルデータベースライブラリ。APIから取得した査定アイテムのカテゴリや、査定対象のブランド情報、製品情報などをRealmに保存しています。
これらのデータで、サーバー側のDBと差分があれば、Realmを更新して、査定できる内容をアップデートしています。
Moyaは通信ライブラリAlamofireのラッパーで通信処理をカプセル化して扱いやすくしてるライブラリ。このMoyaを使い、Service単位でインターフェイスを定義してAPIとの通信処理を実装してます。
これとは別に、openapi-generatorで自動生成したコードを使ってAPIとやりとりを行っていて、新しいAPIではこちらを使うようにしています。
APIのレスポンスはSwiftyJSONを使ってパースをしています。Codableに少しづつ移行していっています。
画像取得やキャッシュ化は、Kingfisher を使っています。
リアクティブで、図のように単一方向ストリームのアーキテクチャを提供するフレームワークです。新しい画面やViewではReactorKitを取り入れて実装しています。
処理の流れがわかりやすく、どこに何を書くのかが決まった設計となるので実装がしやすいです。
gifアニメーションをサポートしてくれるライブラリ。CASHアプリ内にはところどころで表現されるリッチなアニメーションがありますが、gifファイルをGifuを使って再生しています。
CASHアプリ内のアニメーションの中には、一部lottieを使ったものもあります。lottieは、jsonファイルをもとにアニメーションを再生してくれるライブラリです。
カレンダーUIを簡単に表示できるライブラリ。ユーザーがキャッシュ化したアイテムを、自宅に集荷してもらう日付を決める際に利用指定されています。
画面をスワイプしたりタブ選択をすることで簡単に画面遷移ができるContainer ViewControllerを提供してくれるライブラリ。
CASHアプリには、すぐにアイテムを査定できる「いますぐ査定」と、アイテムをまとめて選んで査定ができる「まとめて査定」があります。
「いますぐ査定」と「まとめて査定」ができる画面をXLPagerTabStripを使って切り替えられるようになっています。
「まとめて査定」の施策については、 ↓ で紹介されているので気になる方は読んでみてください。
CASHのiOSアプリで利用している主なライブラリを紹介させていただきました。
よく使われているライブラリが多かったと思いますが、CASHのアプリがどう動いてるか少しだけ知っていただけてでも親しみを感じていただけたのではないでしょうか。
バイセルテクノロジーズでは一緒に開発をしてくれるエンジニアを募集しております。
バイセルテクノロジーズ、CASHに興味を持っていただけた方、是非お待ちしております。