Google Spreadsheetで管理している育児記録にGoogle Formsから情報を登録する

背景

この間、こんなものを作りました。 miyataro.hatenablog.com

今はまだ子供の一ヶ月検診が終わっていなくてずっと家にいるので常にAmazon Echoが近くにあるが、そのうち外出するようになれば買物中におむつを替えたりするはず。
そのときには当然Echoはないのでその場でポチッと登録できるインターフェイスが必要になる。

という事情から、Google Spreadsheetの育児記録にサクッとイベントを登録できる画面を作った。
使用したのはGoogle Formsで、Google Spreadsheetとの連携がすごく簡単にできるので結構すんなり作れた。

実装

Google Formsの作成

育児記録を管理しているSpreadsheetからフォームを作成して編集する。 f:id:miyataro32:20180503212308p:plain

フォームの中身はこんな感じ。 f:id:miyataro32:20180503212011p:plain

Spreadsheet側のGoogle Apps Scriptへトリガー追加

フォームを作成したはいいが、フォーム専用のシートが新しく作成されてそこに送信内容が溜まっていくという仕組みになっている。今回はAlexa経由で登録したときと同じくrecordsというシートに溜まっていってほしい。
調べてみると、Google Spreadsheetに紐付いたGoogle Apps Scriptで利用可能なトリガーの中にForm submitというものがあったのでこれを使う。
Event Objects  |  Apps Script  |  Google Developers

Google Apps Scriptのイベントにはsimpleとinstallableというのがあって、simpleはonOpenのような特定のイベントをハンドルする関数の名前があらかじめ定義されており、その関数を実装すればいいやつみたい。installableなイベントは、設定画面から任意の関数に紐付けることができるらしい。
今回使うForm submitイベントはinstallableなので自分で好きな名前の関数を作ってそいつを設定画面から紐付ける感じになる。

ということで、とりあえずSpreadsheetからスクリプトエディタを開いて関数を作ってみた。(説明のために若干簡単にしてある)

function onFormSubmit(e) {
  var dateKey;
  var eventKey;
  var milkKey;
  
  var keys = Object.keys(e.namedValues);
  keys.forEach(function(key) {
    if (key.indexOf('日時') > -1) {
      dateKey = key;
    } else if (key.indexOf('イベント') > -1) {
      eventKey = key;
    } else if (key.indexOf('ミルク') > -1) {
      milkKey = key;
    }
  });
  
  var date = e.namedValues[dateKey][0] ? new Date(e.namedValues[dateKey][0]) : new Date();
  var events = e.namedValues[eventKey][0].split(/,\s*/);
  var milkVolume = e.namedValues[milkKey][0];
    
  if (events.indexOf(TYPE_NAME.unchi) > -1) {
    records.appendJournalRecordWithSpecificDate(date, TYPE.UNCHI);
  }
  if (events.indexOf(TYPE_NAME.oshikko) > -1) {
    records.appendJournalRecordWithSpecificDate(date, TYPE.OSHIKKO);
  }
  if (events.indexOf(TYPE_NAME.oppai) > -1) {
    records.appendJournalRecordWithSpecificDate(date, TYPE.OPPAI);
  }
  
  if (milkVolume) {
    records.appendJournalRecordWithSpecificDate(date, TYPE.MILK, milkVolume);
  }
}

トリガー実行時にこの関数に渡されるイベントオブジェクトeの中には以下のような値が入っている。

{
  "values": [
    "2018/05/03 21:22:27",
    "2018/05/03 21:20:00",
    "おっぱい",
    ""
  ],
  "namedValues": {
    "日時(空欄の場合は現在の時刻)": [
      "2018/05/03 21:20:00"
    ],
    "イベント(ミルクを飲ませた場合は「ミルクの量」を入力)": [
      "おっぱい"
    ],
    "ミルクの量 (mL)": [
      ""
    ],
    "タイムスタンプ": [
      "2018/05/03 21:22:27"
    ]
  },
(省略)
}

valuesとnamedValuesの両方に入力値が入っている。
どっちを使っても良かったのだが、namedValuesを使ったほうが変更に強そうだったので、namedValuesのキーから必要なものを識別してvalueを取るようなロジックを書いた。

トリガーの設定はスクリプトエディタから編集 > 現在のプロジェクトのトリガーとするとモーダルが開くので、下記のように指定する。
f:id:miyataro32:20180503221456p:plain

これでGoogle Formsから送信するとrecordsシートに情報が溜まっていくようになった。

onFormSubmitの全体はGitHubを参照 github.com

Google Formsにデフォルト値を入れられるようにする

ここまで作って動くようになったものの、妻から新しい要件が出てきた。
日付と時刻の入力はデフォルト値で現在時刻が埋まっている状態から時刻だけちょちょっと変える感じでしたいらしい。

調べてみるととりあえず初期値を入れる方法はあるらしい。 blog.nakachon.com

ただし、現在時刻のように動的に初期値を作る機能はGoogle Formsにはなさそう。

そこで、とりあえず日付と時刻に適当な初期値が入るようなurlを作成した上で、そのurlをもとにSpreadsheet上で動的に現在時刻が入るurlを作りつつそのurlへのリンクを表示し、「登録はこちらから」みたいな感じにしてみた。

まとめ

これでGoogle SpreadsheetとGoogle FormsとAmazon Echoで育児記録を管理する仕組みができた。
全体像はこんな感じ
f:id:miyataro32:20180502201647p:plain

機能的には出来ているが、Amazon Echoから操作した際の応答が若干遅いという問題が未解決のまま残っているので、もし解決できたら記事を書く予定。