読者です 読者をやめる 読者になる 読者になる

ヘルスケアデータを半自動でGCSに投入する

ここ最近クロスバイクで通勤していて、ただ通勤してるだけではつまらないので、AppleヘルスケアにAppleWatchを通して走った距離や消費したカロリーを蓄積している

ただ現状は蓄積してるだけなので何か出来ないかと考えているけど一旦GCSにおく
今回はGCSにアップロードするのにGoogleAppScriptsを使用する

大まかな流れ

  • iphone側でヘルスケアデータの書き出しを行う
  • 共有方法でGmailを使用する(自分のgmailアカウントから自分のgmailアカウントへメールを送ることになります)
  • Gmai側では任意のラベルをつけて、それを対象にしてGASで処理をさせ添付されているデータをGCSへアップロードして完了したラベルをはる

ラベルの設定

f:id:hatappi1225:20170416160425p:plain

ラベルはなんでも良いが今回は上の画像のように GCSエクスポート という親ラベルに 未処理完了 という子ラベルを配置した
今回はiphone側からヘルスケアデータをGmailでおくった時に 未処理 というラベルをつけるようにフィルタを設定する

サービスアカウントの発行

GoogleAppScriptからGCSへファイルアップロードするためにサービスアカウントを発行する
発行の仕方については

Google Cloud Storage の認証  |  Cloud Storage ドキュメント  |  Google Cloud Platform

を見ると良さそうです
ポイントとしてはGCSへの書き込み権限jsonで鍵を取得するの2つが満たされていること

GoogleAppScript

GoogleAppScriptファイルを新規で作成をする
ここにGmailから未処理ラベルがついたメールを取り出して添付ファイルをGCSにアップロードしていく

未処理のラベルをメールを取り出して完了するラベルをはる処理

function main() {
  // serachに使えるのはgmailでの検索フォームと同じ
  const threads = GmailApp.search('label:gcsエクスポート-未処理');
  for(var i = 0; i < threads.length; i++) {
    const messages = GmailApp.getMessagesForThread(threads[i]);
    /* 各メールから日時、送信元、件名、内容を取り出す*/
    const message = messages[messages.length - 1];
    const attachments = message.getAttachments();
    for(var j = 0; j < attachments.length; j++) {
      if (attachments[0].getName() == "書き出す.zip") {
        // ここにファイルアップロード処理をかく
        threads[i].removeLabel(yetLabel);
        threads[i].addLabel(completeLabel);
      }
    }
  }
}

アップロード時には認証を行わなけれならないので すでに先人がAppScript用に配布しているものがあるので今回はこれを使用する

github.com

function uploadFile_(gmailAttachment) {
  // GASのスクリプトプロパティにSAJsonKeyという名前で先程のサービスアカウントのjsonをまるごといれる
  const jsonKey = JSON.parse(PropertiesService.getScriptProperties().getProperty("SAJsonKey"));
  
  const gsAPP = new GSApp.init(jsonKey.private_key, ['https://www.googleapis.com/auth/devstorage.full_control'], jsonKey.client_email);
  const tokens = gsAPP.addUser(jsonKey.client_email).requestToken().getTokens();
  const token = tokens[jsonKey.client_email].token;
  
  const bucketName = '任意のバケット名';
  const uploadFilepath = 'GCSへのアップロードパス exp: hoge/fuga.zip';
  const requestUrl = "https://www.googleapis.com/upload/storage/v1/b/"+bucketName+"/o?uploadType=media&name="+encodeURIComponent(uploadFilepath);
  
  const blob = gmailAttachment.copyBlob();
  const bytes = blob.getBytes();
  
  const fetchOptions = {
    method: "POST",
    contentLength: bytes.length,
    contentType: blob.getContentType(),
    payload: bytes,
    headers:{Authorization:"Bearer "+ token}
  };
  
  UrlFetchApp.fetch(requestUrl, fetchOptions);
}

最終的なGoogleAppScriptの中身は下記のようになる

gist.github.com

あとはGoogleAppScriptには定期実行する cronのような機能があるので任意の時間で発火させるだけで可能である

f:id:hatappi1225:20170416165001p:plain

これで実行すると未処理のファイルがあるとGoogleAppScriptが定期処理の際に処理にてGCSへファイルをアップロードしてくれる

f:id:hatappi1225:20170416165043p:plain

おまけ

運用するにあたっては処理開始したのか?完了したのか?わからないのでslackに通知なげてます

f:id:hatappi1225:20170416170325p:plain