Getting Started

2020/10/09

OK、Riiiver! 今日何回怒った?

どうも、こんにちは!JellyWareです。

Getting Started!! Riiiver tutorialの第18弾となります。

このシリーズは、Riiiverやプログラミングを学び、初心者の方でもEco-Drive Riiiverを活用したオリジナルアプリを理解して作れるようになることを目指します。

今回は、スプレッドシートをデータベース代わりにして怒った回数を記録できるようにします。条件分岐を活用して、怒った回数や実行する時間に応じて処理を変えていきます。

まずは基礎となるところを解説します。


どんなモノができる?

以下が完成品のイメージです。

Eco-Drive Riiiver をアレンジして、今日何回怒ったかをカウントします。同時に、怒った回数を通知させます。スプレッドシートに記録していくので、グラフ化させることもできます。日々の記録から怒りっぽさを自覚して改善していきましょう

目次


Google Cloud Platform

今回はGASではなく、Google Sheets APIを活用します。GASを使っても良いのですが、別の方法として説明します。

Google Sheets APIを使うメリットとしては、Node.js側で全て操作を行えることです。GASの場合は、スプレッドシート側にスクリプトを用意する必要がありましたが、今回はNode.jsだけで完結できます。

Google Sheets APIを使用するには、Google Cloud Platformに登録する必要があります。使用するには費用がかかりますが、1年間の無料枠があるので、今回テスト使用する分には費用は発生しません。

アカウント作成

それではGoogle Cloud Platformにアクセスしましょう。また、今後Google Cloud PlatformはGCPと省略します。

GCPアカウントは、Googleアカウントと紐づきます。Chromeを使用しているなどで、Googleアカウントにログイン済みの場合はそのアカウントが自動で使用されるので注意してください。

無料で開始をクリックします。

Googleアカウントにログインしてください。既にログイン済みの場合は、この画面は表示されません。

在住している国を選び、利用規約のチェックボックスをつけて続行をクリックします。

アカウントの種類は「個人」にしてください。その他、「名前と住所」「クレジットカードの情報」と順に入力してください。

画面にも記載されていますが、有料版へは手動で切り替えない限り支払いは発生しません。

登録が終わると、このような管理画面に移ります。

プロジェクト作成

GCPでは「プロジェクト」単位で使用するサービスを管理できます。今回のようにテスト的に使用する分には効率が悪く感じられますが、複数のアプリでGCPを利用する場合、各アプリ毎にプロジェクトを作成できて楽です。

左上のロゴの右隣にあるボタンをクリックして新しいプロジェクトをクリックします。

適当にプロジェクト名を入力します。あらかじめ入力されている名前は変更しても大丈夫です。ここではtestにしました。

入力したら作成をクリックします。

APIの有効化

これでプロジェクトの作成完了です。次に、作成したプロジェクトで「Google Sheets API」を有効化させます。

まずは、先ほど作成したプロジェクトに切り替えます。ロゴの右側をクリックして、作成したプロジェクトをクリックします。

ロゴの右側に作成したプロジェクト名が表示されていれば切り替え成功です。

次に左側のメニューのAPIとサービス > ライブラリ をクリックします。

ここで使用するAPIを追加できます。「Google Sheets API」と検索します。

検索結果をクリックしてください。

最後に有効にするをクリックして完了です。

認証情報の取得

次に、APIを使用するための認証情報を取得します。

有効化後のページで認証情報を作成をクリックします。

使用するAPIはGoogle Sheets APIを選択します。

API を呼び出す場所はウェブサーバー(node.js、Tomcat など)を選びます。
アクセスするデータの種類はアプリケーションデータを選びます。
App Engine または Compute Engine でこの API を使用する予定はありますか?は、いいえ、使用していませんを選びます。

最後に必要な認証情報をクリックします。

次にこのサービス専用のアカウントを作ります。アカウント名はなんでも大丈夫です。

ロールはProject > 閲覧者を選びます。
キーのタイプはJSONを選びます。

最後に次へをクリックすると認証情報がまとめられたJSONファイルをダウンロードされるので、大切に保管しておいてください。

請求の無効

念の為、GCPでの請求を無効とする設定方法をまとめます。

お支払いをクリックします。

請求先アカウントを管理をクリックします。

左上のマイ プロジェクトをクリックします。

対象のプロジェクトの課金を無効にするをクリックすれば、設定変更できます。

今回作成したプロジェクトは、既に無効になっていると思います。

google-spreadsheet

google-spreadsheetとは、上記で設定したGoogle Sheet APIをNode.jsで使用するためのモジュールです。

  1. スプレッドシートの指定
  2. 認証処理
  3. スプレッドシートの情報取得
  4. シートの取得
  5. セルに値を記入したり、取得したりする

基本的に上記の流れとなっていて、決まりとして書くコードが少し多いです。

準備

glitchのプロジェクト作成とスプレッドシートの準備をします。

次に、認証に必要な情報を.envに書き込んでいきます。.env内に記載されている内容は、プロジェクト作成者本人しか確認することができません。

まずは.envを開いてSECRETMADE_WITHを、それぞれprivate_keyclient_emailに書き換えます。

Variable Valueには、ダウンロードしたJSONファイルに記載されている値を記載してください。

最後にgoogle-spreadsheetをモジュールを追加します。

これでglitch側の準備はOKです。次にスプレッドシートを作成しましょう。

スプレッドシートを作成したら、リンクを知っている全員編集者に変更します。GoogleアカウントとGCPのアカウントは紐づいていますが、権限が同じになるわけではないので注意してください。

これで準備OKです。

スプレッドシートの指定

まずは、スプレッドシートの指定方法について説明します。スプレッドシートの指定には「スプレッドシートキー」が必要です。

スプレッドシートキーは、URLのhttps://docs.google.com/spreadsheets/d//edit#gid=0で挟まれている文字列です。

それでは、実際にコードを確認しましょう。

// モジュール読み込み
const {GoogleSpreadsheet} = require("google-spreadsheet");

// スプレッドシートキー
const spreadSheetKey = "xxxxxxxxxxxxx"; // ここを差し替える

// スプレッドシートの指定
const doc = new GoogleSpreadsheet(spreadSheetKey);

今までと異なり、モジュールを読み込む際には{GoogleSpreadsheet}{}を使う必要があります。理由はimportのフォーマットで決まっているためです。参考

const doc = new GoogleSpreadsheet(spreadSheetKey);

重要なのはこの1行で、読み込んだモジュールとスプレッドシートキーを使って取得したいスプレッドシートを指定します。

認証処理

認証処理には、先ほど.envに追加した情報を使います。

// モジュール読み込み
const {GoogleSpreadsheet} = require("google-spreadsheet");

// スプレッドシートキー
const spreadSheetKey = "xxxxxxxxxxxxx"; // ここを差し替える

// スプレッドシートの指定
const doc = new GoogleSpreadsheet(spreadSheetKey);

const doSpreadSheet = async () => {
    // 認証
    await doc.useServiceAccountAuth({
        client_email: process.env.client_email,
        private_key: process.env.private_key.replace(/\\n/g, '\n')
    });
}

doSpreadSheet();

スプレッドシートの指定までは同じです。

await doc.useServiceAccountAuth({
    client_email: process.env.client_email,
    private_key: process.env.private_key.replace(/\\n/g, '\n')
});

スプレッドシートの中身を取得するため、useServiceAccountAuth関数を使って認証処理を行います。非同期処理なのでawaitを活用しています。認証に必要な情報としてclient_emailprivate_keyを渡します。

引数には.envから読み込んだ値を追加しています。glitchではprocess.env.名前と記載するだけで値を取得できます。またprivate_keyの値は改行コード\nが文字列になっているのでreplace関数を使って文字コードに置換しています。

(非同期処理については、こちらをご参照ください)

スプレッドシートの情報取得、シートの取得

認証処理に成功すると、スプレッドシートの情報を取得できるようになります。タイトルや各シートの情報などが含まれています。

また、合わせてシートの取得も行います。

// モジュール読み込み
const {GoogleSpreadsheet} = require("google-spreadsheet");

// スプレッドシートキー
const spreadSheetKey = "xxxxxxxxxxxxx"; // ここを差し替える

// スプレッドシートの指定
const doc = new GoogleSpreadsheet(spreadSheetKey);

const doSpreadSheet = async () => {
    // 認証
    await doc.useServiceAccountAuth({
        client_email: process.env.client_email,
        private_key: process.env.private_key.replace(/\\n/g, '\n')
    });
    
    // スプレッドシートの情報取得
    await doc.loadInfo();
    
    // シートの取得
    const sheet = doc.sheetsByIndex[0];
}

doSpreadSheet();

認証処理までは同じです。

// スプレッドシートの情報取得
await doc.loadInfo();

loadInfoを使って取得します。非同期処理のためawaitが必要です。

この処理を挟まないと以降の処理が実行できないので気をつけてください。

// シートの取得
const sheet = doc.sheetsByIndex[0];

シートを取得します。doc.sheetsByIndex[0];0を変更することで指定するシートを変更できます。

指定する数値とシートは、タブの位置と関係しています。左側から0、1、2…となります。

ヘッダーの追加

ここまでは、glitchで実行されても何も起きなかったと思います。それでは取得したシートに書き込みを行っていきます。

まずは、1行目に書き込みを行います。google-spreadsheetでは、1行目を「ヘッダー(見出し)」として扱えます。

// モジュール読み込み
const {GoogleSpreadsheet} = require("google-spreadsheet");

// スプレッドシートキー
const spreadSheetKey = "xxxxxxxxxxxxx"; // ここを差し替える

// スプレッドシートの指定
const doc = new GoogleSpreadsheet(spreadSheetKey);

const doSpreadSheet = async () => {
    // 認証
    await doc.useServiceAccountAuth({
        client_email: process.env.client_email,
        private_key: process.env.private_key.replace(/\\n/g, '\n')
    });

    // スプレッドシートの情報取得
    await doc.loadInfo(); 

    // シートの取得
    const sheet = doc.sheetsByIndex[0];

    // ヘッダーの追加
    await sheet.setHeaderRow(["日にち", "怒った回数"]);
}

doSpreadSheet();

上記のコードを試してください。

成功すると、このようにシートに記入されます。

await sheet.setHeaderRow(["日にち", "怒った回数"]);

setHeaderRow関数を使用することでヘッダーを追加できます。引数は配列にしてください。また、非同期処理なのでawaitが必要です。

行の追加

行の追加を行います。先ほど追加したヘッダーは削除しないでください。

// モジュール読み込み
const {GoogleSpreadsheet} = require("google-spreadsheet");

// スプレッドシートキー
const spreadSheetKey = "xxxxxxxxxxxxx"; // ここを差し替える

// スプレッドシートの指定
const doc = new GoogleSpreadsheet(spreadSheetKey);

const doSpreadSheet = async () => {
    // 認証
    await doc.useServiceAccountAuth({
        client_email: process.env.client_email,
        private_key: process.env.private_key.replace(/\\n/g, '\n')
    });

    // スプレッドシートの情報取得
    await doc.loadInfo(); 

    // シートの取得
    const sheet = doc.sheetsByIndex[0];

    // 行の追加
    await sheet.addRow([new Date(), 0]);
}

doSpreadSheet();

setHeaderRowaddRowに変更されただけです。同じく、引数は配列で非同期処理です。

このように最終行に追加されていきます。

最終行の取得

最終行の取得を行います。シートは、2行目のA2 B2セルに何かしら入力されていればOKです。

// モジュール読み込み
const {GoogleSpreadsheet} = require("google-spreadsheet");

// スプレッドシートキー
const spreadSheetKey = "xxxxxxxxxxxxx"; // ここを差し替える

// スプレッドシートの指定
const doc = new GoogleSpreadsheet(spreadSheetKey);

const doSpreadSheet = async () => {
    // 認証
    await doc.useServiceAccountAuth({
        client_email: process.env.client_email,
        private_key: process.env.private_key.replace(/\\n/g, '\n')
    });

    // スプレッドシートの情報取得
    await doc.loadInfo(); 

    // シートの取得
    const sheet = doc.sheetsByIndex[0];

    // 行情報の取得
    const rows  = await sheet.getRows();

    // 最終行の各セルの取得
    const lastDate = rows.slice(-1)[0]["日にち"];
    const angryNumber = rows.slice(-1)[0]["怒った回数"];

    console.log(lastDate);
    console.log(angryNumber);
}

doSpreadSheet();

シートの取得までは同じです。シートの最終行の値が出力されます。

// 行情報の取得
const rows  = await sheet.getRows();

まずは、行全体を取得しています。各行の情報がオブジェクトで、それらが配列でまとめられています。以下のようなイメージです。

[
      {
          "行数": 1
          "日にち": "8月26日",
          "怒った回数": 0   
      },
      {
          "行数": 2
          "日にち": "8月27日",
          "怒った回数": 3   
      },
      {
          "行数": 3
          "日にち": "8月28日",
          "怒った回数": 5   
      }
  ]

各見出しに対するセルの入力情報がまとめられている、最終行は最後にあるのがポイントです。

// 各セルの取得
const lastDate = rows.slice(-1)[0]["日にち"];
const angryNumber = rows.slice(-1)[0]["怒った回数"];

結果としてこのようにすることで値を取得できます。slice関数については後ほど解説します。

行の更新

行の更新を行います。シートは、2行目のA2 B2セルに何かしら入力されていればOKです。

// モジュール読み込み
const {GoogleSpreadsheet} = require("google-spreadsheet");

// スプレッドシートキー
const spreadSheetKey = "14vt56dNjbMj2RrLQSz4o6WlfhwnwzVW4hB3nymRBHEo"; // ここを差し替える

// スプレッドシートの指定
const doc = new GoogleSpreadsheet(spreadSheetKey);

const doSpreadSheet = async () => {
    // 認証
    await doc.useServiceAccountAuth({
        client_email: process.env.client_email,
        private_key: process.env.private_key.replace(/\\n/g, '\n')
    });

    // スプレッドシートの情報取得
    await doc.loadInfo(); 

    // シートの取得
    const sheet = doc.sheetsByIndex[0];

    // 行情報の取得
    const rows  = await sheet.getRows();

    // 最終行の怒った回数を取得
    const angryNumber = rows.slice(-1)[0]["怒った回数"];

    // 行の更新
    rows.slice(-1)[0]["怒った回数"] = 1;
    await rows.slice(-1)[0].save();
}

doSpreadSheet();

最終行の取得までは同じコードです。

rows.slice(-1)[0]["怒った回数"] = 1;
await rows.slice(-1)[0].save();

取得した時と同様にrows.slice(-1)[0]['怒った回数']を使用していますが、今度は代入しています。

そして、save関数を使用することで更新がシートに反映されます。

これでgoogle-spreadsheetモジュールの使い方については終了です。他にも色々活用できるので興味がある方は調べてみてください。

slice関数

先ほど使用したslice関数について解説します。

const array = [1, 2, 3, 4];
const lastArray = array.slice(-1);

console.log(lastArray) // [4] と出力される

slice関数は、引数を-1とすることで、配列の最後の要素を抜き出して新しい配列を作成できます。

上記だと、[1, 2, 3, 4]という配列から4が抜き出されて[4]という配列が作成されます。

const array = [1, 2, 3, 4];
const lastArray = array.slice(-1);
const last = lastArray[0];
console.log(last) // 4 と出力される

配列の中身を取り出したい場合は、インデックスで0を指定すればOKです。

rows.slice(-1)[0]["怒った回数"]

スプレッドシートでは、さらにヘッダーを指定することで最終行の「怒った回数」を取得していました。

new Date()

タイムスタンプで使用していたnew Date()について少し説明します。

const date = new Date();
console.log(date);

2020-08-27T03:16:13.439Zという形式で日時を取得できます。また、この時間は「協定世界時」という世界基準となる時間で、日本時間より9時間遅いです。

const date = new Date();
const jstTime = date.getHours() + 9;
date.setHours(jstTime);// 日本時間に変換
console.log(date);

日本時間に変換するには9時間増やす必要があります。

dateに対してgetHours()を使用することで「時間」を取得できます。これに9を足して日本時間にします。そして、dateに対してsetHoursを使用することで、引数で指定した時間へ上書きできます。上記ではjstTimeで上書きしています。

次に「年」「月」「日」のみを抜き出して、形式を整えます。

const date = new Date();
date.setHours(date.getHours() + 9);// 日本時間に変換
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();

console.log(`${year}${month}${day}日`);

2020年8月27日という形式に変換しました。

dateに対してgetFullYear() getMonth getDate を使用することで「年」「月」「日」をそれぞれ取得できます。

ただし、月に関しては 0 ~ 11 と1つずれているので+ 1が必要です。英語圏では日本のように月を数字で表すのが一般的ではないため、配列のように0からスタートしています。


今回は、一旦ここまでとします。次回、実際に作成した「Piece JSON」と「Piece Func」について解説します。

ご覧くださいましてありがとうございました。