kun432's blog

Alexaなどスマートスピーカーの話題中心に、Voiceflowの日本語情報を発信してます。たまにAWSやkubernetesなど。

〜スマートスピーカーやVoiceflowの記事は右メニューのカテゴリからどうぞ。〜

Voiceflow TIPS #18 Googleスプレッドシート連携で作るゼロカロリースキルもどき 〜スプレッドシートの更新〜

Voiceflow夏休みAdvent Calendarの18日目です。

Googleスプレッドシート連携シリーズ第4回です。検索・登録機能まで実装した「ゼロカロリースキル」に「更新」機能を追加していきたいと思います。

前回のあらすじはこちら。

ではまずスキルの実行イメージから。


アレクサ、「ゼロカロリースキル」をひらいて

ゼロカロリースキルです。カロリーを知りたい食べ物の名前を言ってください

カツカレーを更新

カツカレーは、カツとカレーのカロリーが相殺されるから、という理由で、現在登録されています。更新する理由をお聞きします。最初に、理由、最後に、から、をつけていってください。例えば、理由、ほにゃほにゃだから、という感じで言ってください。

理由、カツとカレーのカロリー同士が喧嘩して相殺されるから

カツカレーは、カツとカレーのカロリー同士が喧嘩して相殺されるから、カロリーゼロ、で更新します。よろしいですか?

はい

カツカレーを更新しました。カロリーを知りたい食べ物の名前を言ってください・・・


さぁ、ではやってみましょう。

スキルの全体図

f:id:kun432:20190818182854p:plain

前回と同様に、更新用のフローを別に追加します。線が徐々にすごいことになってきてますね・・・こうならないように実際にはFlow Block使って処理ごとにフローを分けることをおすすめしますが、今回は全体のわかりやすさを優先してます。

Interactin Blockへ更新フローの追加

まずは、最初から更新を目的とする場合です。前回と同じように、更新用のフローを新たに作っていきますので、更新用のフローへの入口となるインテントを追加します。Home -> Speak の次にあるInteraction Blockをクリックします。

f:id:kun432:20190818184537p:plain

Interaction Blockの設定を順に見ていきます。Slotタブは今回も変更不要です。

f:id:kun432:20190818184914p:plain

Intentsタブに新たに更新用インテントのサンプル発話を登録していきます。"+ Add Intent"をクリックして、インテント名は"update_intent"、サンプル発話は以下のような感じで登録します。

f:id:kun432:20190818184927p:plain

Choicesタブに更新用のフローを追加します。"+ Add Choice"をクリック、ドロップダウンから先ほど作成した"update_intent"を選択、"+ Add Variable Map"をクリックして、他のインテントと同じように、スロット"[slot_recipe]"(食べ物名を受け取るスロット)と変数”varRecipe”(食べ物名を入れておくフロー変数)のマッピングを作成します。

f:id:kun432:20190818184937p:plain

これで、更新用フローができました。ここからブロックを繋げていきます。

更新フローの作成

f:id:kun432:20190818185305p:plain

更新の場合、すでに食べ物名が登録されていることが前提となります。が、万が一、登録されていない場合はどうなるでしょうか?ということで、一旦登録されているかどうかのチェックを行います。

Integration Blockを使って食べ物名の存在チェックをしましょう。Integration Blockをおいて、先程のInteraction Blockから線をつなげます。

f:id:kun432:20190818191531p:plain

Integration Blockの設定を行います。基本的にやっていることは「検索」と同じです。

f:id:kun432:20190818192345p:plain

で、スプレッドシート検索の結果と変数と紐付ける Mapping Output のところですが、後でやる「更新」の場合に備えて、少しだけ前のやり方と変わります。順にやっていきます。"+ Add Mapping"をクリックして、

f:id:kun432:20190818193636p:plain

カラムのところは"Row Number"を選択します。"Row Number"はスプレッドシートの何行目か?を取得するために予め用意されているビルトインの設定で、これを取得するようにします。で、これを保存しておく変数を作ります。変数を選択するドロップダウンの一番下に"Create variable"があるのでこれをクリックします。

f:id:kun432:20190818193942p:plain

左のメニューが変数のメニューに変わりますので、ここでフロー変数"varRowNum"を作成します。

f:id:kun432:20190818194022p:plain

再度、Integration BlockのMapping Outputの設定に戻って、カラム"Row Number"と変数"varRowNum"を紐付けます。

f:id:kun432:20190818194141p:plain

最後に、検索時と同じようにカラム"reason"と変数"varReason"を紐付けるマッピングを追加すればOKです。

f:id:kun432:20190818194242p:plain

最終的にはこんな感じです。

f:id:kun432:20190818194453p:plain

ちょっと見にくいので、Expandで表示したものも貼っておきます。

f:id:kun432:20190818194729p:plain

では、Test Integrationをクリックしてテストしてみます。

f:id:kun432:20190818200137p:plain

適当にすでに登録済みのものを入力してみてください。今回は「ラーメン」で検索してみました。

f:id:kun432:20190818200202p:plain

検索のときと結果は変わらないですよね。1点だけ補足しておくと、先程Mapping Outputで設定した"Row Number"はこの結果の"row"の値になります。

f:id:kun432:20190818200355p:plain

実際のスプレッドシートの方も見てみましょう。確かに行番号が取れているのがわかります。更新時は、この行番号をとっておくことが重要になります。

f:id:kun432:20190818200500p:plain

スプレッドシートの検索処理に失敗した場合のフローも用意しておきます。ただし、このフローは結果が0件の場合ではなく、あくまでもスプレッドシートへのアクセスで何か問題があった場合なのでご注意ください。

f:id:kun432:20190818201036p:plain

スプレッドシートの検索結果をチェックします。If Blockをおいて、以下のように、"varReason"の値が定義されていない、すなわち、検索結果が0件だったかどうかをチェックする条件を入力します。ここは expression を使ってください(expressionの使い方はTIPS #12の「⑤⑥⑦ If Blockで検索結果の判定」を参照してください)

f:id:kun432:20190818201631p:plain

ちょっと条件のところは見にくいのでいかに貼っておきます。変数のところはそのままコピペでは動かないので注意してくださいね。

equal({varReason},undefined)

"varReason"の値が定義されていない、すなわち、検索結果が0件だった場合は、更新ができないので、その旨を発話させて、

f:id:kun432:20190818201733p:plain

最初のInteraction Blockに戻してやります

f:id:kun432:20190818201748p:plain

ということで、お気づきになられた方がいらっしゃるかもしれませんが、これ、検索フローでやったこととほとんど同じです。

f:id:kun432:20190818203159p:plain

なので、こういう場合は、ブロックのコピペを使うと良いです。ということで、先程のIf BlockとSpeak Blockをコピペで作る手順もご紹介しましょう。

ブロックのコピー&ペースト

この状態から・・・

f:id:kun432:20190818203355p:plain

Shiftキーを押しながら、検索フローのIf Block と Speak Blockをクリックします。少しだけハイライトされているのがわかりますか?(違いが見えにくいですよね・・・これは既に開発元に修正要望上がってるのでそのうち治るかもしれません。)

f:id:kun432:20190818204259p:plain

この状態で、WindowsならCtrl+C、MacならCommand+Cを入力すると、以下のように選択したブロックがクリップボード上にコピーされたことがわかります。

f:id:kun432:20190818204311p:plain

ペーストしたい場所がだいたい中心になるようにCanvasを動かして、WindowsならCtrl+V、MacならCommand+Vを入力してみてください。すると、

f:id:kun432:20190818204748p:plain

あとは、位置を整えて、線を引いて、文言等修正しないといけないところは修正すればOKです!かんたんですね! If BlockでExpression使う場合とか、Code Blockで似たようなコードを書く場合などは、煩雑だしミスも起こりやすいと思うので、コピペもうまく活用してもらえればと思います。

少し脱線しましたが、続けましょう。If Blockで登録されていることが確認できたら、更新の準備ができたことになりますので、更新後の理由を取得していきます・・・ってこれも聞いたことありますよね?

これ、登録時のフローとほぼ同じですね!ということは、ここフローごとマルっとコピペして修正すればOKですね!

先程は、Shiftキーを押しながら、ブロックを順にコピーしましたが、Shiftキーを押しながら範囲選択してコピーすることもできます。

f:id:kun432:20190818213945p:plain

f:id:kun432:20190818214127p:plain

で、ちょっとCanvasを動かしてペーストします。

f:id:kun432:20190818214435p:plain

同じフローがまるっとコピーされました!範囲選択した範囲内に限り、線もコピーされていますね。あとは、更新フローからつなげて、修正していけばOKというわけですね。楽ちんです!順次修正していきます。

更新フローの作成(続き)

まず、最初のSpeak Block。ここは文言の修正だけです。登録時は理由の発話の仕方だけを説明していましたが、更新時は今登録されている理由の内容も聞いた上で、理由の発話について説明します。

f:id:kun432:20190818220802p:plain

次、実際に理由を取得するInteraction Block。ここは変更する必要なしです。理由を取得するだけなので、登録時も更新時もやることは全く変わりませんので、同じインテントをそのまま使えます。

f:id:kun432:20190818221015p:plain

更新の確認を発話するSpeak Block。ここも「登録」を「更新」に変えるだけです。

f:id:kun432:20190818221125p:plain

確認に対して「はい」「いいえ」を受け取るInteraction Block、ここも変更する必要はありません。

f:id:kun432:20190818221238p:plain

で、「いいえ」の場合、ここも「登録」を「更新」に変更するだけです。そして、最初の検索のInteraction Blockに戻します。

f:id:kun432:20190818221500p:plain

f:id:kun432:20190818221511p:plain

では、Integration Blockで更新処理です。

Integration Blockでスプレッドシートの更新

Integration Blockを登録フローからコピペしてきたので、現在はスプレッドシートへの(新規)登録になっています。

f:id:kun432:20190818222816p:plain

これを更新処理に変更していきます。まず最初の”I want to”のところ、更新なので"Update Data"を選択します。

f:id:kun432:20190818230241p:plain

アカウントは選択するだけです。

f:id:kun432:20190818230257p:plain

スプレッドシートの設定が消えているので、検索・登録と同じように設定してください。

f:id:kun432:20190818230616p:plain

次に、With Settingで条件を設定するのですが、設定項目が、検索の時とも登録の時から変わってますよね?

f:id:kun432:20190818230757p:plain

更新時は、「何行目("Row Number")のカラム("recipeName"と"reason")をどう更新するか?」という設定の仕方になります。したがって、スプレッドシートの「何行目」のデータなのか?ということが必要になります。更新フローの最初で「何行目」か?を事前に取得していたのはこのためなのです。

ということで、必要なデータは全て揃っていますよね?それぞれ入力しましょう。Row Numberは"{varRowNum}"、recipeNameは"{varRecipe}"、reasonは"{varReason}"というふうに、変数を設定します。終わったらNextをクリックします。

f:id:kun432:20190818231307p:plain

では、Test Integrationでテストしてみましょう。

f:id:kun432:20190818231604p:plain

更新の場合は、既存のデータを変更することになりますので、こういう警告が出ます。この辺の配慮は嬉しいですね。Confirmをクリックして進めましょう。

f:id:kun432:20190818231710p:plain

では更新します。既に登録されている食べ物名、かつ、その行番号を設定して、RUNをクリックします。

f:id:kun432:20190818232351p:plain

以下のように"Updated"となっていればOKです。

f:id:kun432:20190818232535p:plain

スプレッドシートの方も更新されていますね!

f:id:kun432:20190818232558p:plain

はい、これで今日のメインテーマであるスプレッドシートの更新処理は完了です!残りのフローも修正していきましょう。

スプレッドシート更新エラーの場合のメッセージも「登録」から「更新」に変えます。

f:id:kun432:20190818233437p:plain

スプレッドシート更新に成功した場合もメッセージも「登録」から「更新」に変えます。そして、最初の検索のInteraction Blockに戻すように線をつなげます。

f:id:kun432:20190818233452p:plain

f:id:kun432:20190818233510p:plain

はい、これで更新フローは完了です。おつかれさまでした!

更新フローのテスト

ではテストです。

f:id:kun432:20190819005007p:plain

うまく更新できていますね!登録されていないものを更新しようとした場合のテストもやっておきましょう。

f:id:kun432:20190819005748p:plain

うまくいきませんね・・・・なぜかスプレッドシート自体へのアクセスが失敗した流れになってしまいます・・・検索フローのときはきちんとできていたのに・・・

f:id:kun432:20190819005823p:plain

で、調べててみたんですが、更新の場合は更新対象となるレコードの行番号が必要なので、row numberを取得するようにしているんですが、どうもこれがあると、Integration Blockの時点でfailになってしまう様子。ここちょっと不具合な気もします。ということで暫定ですが、ここだけ少し線を変えておきましょう

f:id:kun432:20190819010433p:plain

はい、これでテストします。

f:id:kun432:20190819010406p:plain

とりあえず良しですかね。ここは開発元にも確認投げてますので、回答きたらまた修正したいと思います。


ということで、検索・登録・更新と完了しましたので、残りは削除ですね。これでGoogleスプレッドシートでできることは一通りご紹介できたことになると思います。よろしければ次回もご覧くださいませ。