AppSheetと25行のGoogle Apps Scriptで作るバーコード蔵書管理アプリ

Googleのノーコード開発ツールAppSheet、Googleスプレッドシート、Google Apps Scriptを組み合わせて、蔵書管理アプリを無料で作りました。スマホのカメラを使い、バーコードを読むだけで登録できるのでとても便利です！

https://twitter.com/takatama_jp/status/1629890538420994049

[以前は画面の再読み込みが必要なことがあった](https://qiita.com/takatama/items/fa141cb13ccc7324f887)のですが、[AppSheetから直接Apps Scriptを呼び出す機能](https://developers-jp.googleblog.com/2022/05/apps-script-connector-for-appsheet.html)を使うことで再読み込みが不要になり、バーコードを読めば画面が確実に更新されるようになりました。AppSheetでバーコードからISBNを読み取り、Apps Scriptで書籍の情報を追記する、という連携をしています。

次の手順で作っていきます。
1. Google Books APIの動作を確認
1. Googleスプレッドシートを作成
1. スタンドアロンのApps Scriptを作成
1. AppSheetアプリを作成

# Google Books APIの動作を確認

[Google Books API](https://developers.google.com/books/docs/v1/using?hl=ja)を使って、ISBNから書籍の情報を取得します。情報を参照するだけならAPI Keyは不要です。

利用するパラメーターは国コードを表す`country`と、ISBNで検索する`q=isbn:`の2つです。書籍「リーダブルコード」なら`country=jp`、`q=isbn:9784873115658`として、URL https://www.googleapis.com/books/v1/volumes?country=jp&q=isbn:9784873115658 を作ります。

# Googleスプレッドシートを作成

書籍の情報を記録できるよう、新しくGoogleスプレッドシートを作成します。スプレッドシート名は`MyLibrary`、シート名は`Books`に変更しました。

次に、列を設計していきます。AppSheetを使いこなすには、データベースを設計する「データモデリング」がとても重要です。

Google Books APIから返却されるJSONを抜粋すると、次のようになっています

```json
{
  "items": [{
    "volumeInfo": {
      "title": "リーダブルコード",
      "subtitle": "より良いコードを書くためのシンプルで実践的なテクニック",
      "authors": [
        "Dustin Boswell",
        "Trevor Foucher"
      ],
      "publisher": "O'Reilly Media, Inc.",
      "publishedDate": "2012-06",
      "description": "読んでわかるコードの重要性と方法について解説",
      "imageLinks": {
        "thumbnail": "http://books.google.com/books/content?id=Wx1dLwEACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api"
      }
    }
  }]
}
```

そこで、以下の9つの列を準備します。最初のISBNをキーにして「同じISBNの本は一冊しか蔵書にない」とします（同じISBNの本を読み取るとエラーになります）。最後にcreatedAtを追加して、この列を作成した時刻を入れることにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/9caadc41-dc20-711a-3b46-930467ace22a.png)

| ISBN | title | subtitle | authors | publisher | publishedDate | description | thumbnail | createdAt |
|---|---|---|---|---|---|---|---|---|

# スタンドアロンのApps Scriptを作成

続いて、Apps Scriptを作成します。ISBNを指定するとGoogle Books APIを使ってシートに書籍の情報を追記するスクリプトです。

ここで、大事な注意事項があります。AppSheetから呼び出せるのは**スタンドアロンのApps Script**です。（スプレッドシートの拡張機能からApps Scriptを作るのではなく）単独のApps Scriptを作成しましょう。Google Drive上で右クリックをして、Google Apps Scriptを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/6b7c68fb-fbef-9913-bbb6-f6a25ab4877e.png)

名前は`MyLibrary`にしました。作成したApps Scriptをダブルクリックしてスクリプトエディターを開き、次のコードを記述します。`SHEET_URL`には先程作成したスプレッドシートのURLを記述します。

```javascript
// 注意！スプレッドシートのURLに書き換えてください
const SPREADSHEET_URL = 'https://docs.google.com/spreadsheets/d/XXXXXXXXXXXXXXXXXXX/edit#gid=0';

function fetchBookInfo(isbn) {
  const url = `https://www.googleapis.com/books/v1/volumes?country=jp&q=isbn:${isbn}`;
  const res = UrlFetchApp.fetch(url);
  const json = JSON.parse(res.getContentText());
  return json.items[0].volumeInfo;
}

function updateBook(isbn) {
  const sheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL).getSheetByName('Books');
  const rowIndex = sheet.getDataRange().getValues().findIndex(row => row[0] == isbn); // ISBNが数字or文字なので===ではなく==で比較。
  if (rowIndex === -1) throw `ISBNが"${isbn}"の行がありません。`;
  const info = fetchBookInfo(isbn);
  const row = [isbn, info.title, info.subtitle, info.authors.join(', '), info.publisher, new Date(info.publishedDate), info.description, info.imageLinks.thumbnail, new Date()];
  sheet.getRange(rowIndex + 1, 1, 1, row.length).setValues([row]);
}

function testFetchBookInfo() {
  console.log(fetchBookInfo(9784873115658));
}

function testUpdateBook() {
  updateBook(9784873115658)
}
```

`fetchBookInfo(isbn)`は、指定したISBNの書籍の情報をGoogle Books APIを使って取得し、JSON形式で返却します。
`updateBook(isbn)`は、`Books`シートの1列目にISBNが記述されているとき、そのISBNの書籍の情報をその行に追記します。

記述できたら保存して、`testFetchBookInfo()`を実行しましょう。権限が求められたら許可をします。実行ログに書籍の情報が表示されればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/9c55489c-c3f2-7333-d68d-34fcca37e557.png)

次に`testUpdateBook()`を実行します。すると、次のようなエラーが実行ログに表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/eabc469a-ef00-9101-2830-ecad1cedb1ba.png)

`Books`シートにISBN`9784873115658`の行が存在しないためエラーになりました。AppSheetがバーコードを読み取ってISBNを`Books`シートに記入し、それをトリガーにApps Scriptで書籍の情報を取得する、という前提でApps Scriptを準備しています。

そこで、`Books`シートのISBN列に`9784873115658`を記述します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/1542c6e4-28b0-688e-e3df-0b65d1b7ee4f.png)

再度、スクリプトエディターで`testUpdateBook()`を実行します。今度は実行ログにエラーが表示されず、`Books`シートに情報が追記されました（もしエラーが表示される場合は、この先で対処法を示します）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/50b4ecc4-1305-38b9-2c0f-e3a56bcf76bb.png)

| ISBN | title | subtitle | authors | publisher | publishedDate | description | thumbnail | createdAt |
|---|---|---|---|---|---|---|---|---|
| 9784873115658 | リーダブルコード | Dustin Boswell, Trevor Foucher | より良いコードを書くためのシンプルで実践的なテクニック | O'Reilly Media, Inc. | 2012-06 | 読んでわかるコードの重要性と方法について解説 | ![image](http://books.google.com/books/content?id=Wx1dLwEACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api) | 時刻 |

もし実行ログに次のエラーが表示される場合、これまでの作業が十分か確認してください。

- <font color="red">Exception: Unexpected error while getting the method or property openByUrl on object SpreadsheetApp.</font>
  - `SPREADSHEET_URL`にスプレッドシートのURLを正しく記述したか、確認してください。
- <font color="red">TypeError: Cannot read properties of null (reading 'getDataRange')</font>
  - スプレッドシートのシート`シート1`の名前を`Books`に変更したか、確認してください。

# AppSheetを作成

Apps Scriptが正常に動作することを確認できたら、AppSheetのアプリを作成します。AppSheetアプリがバーコードを読み取って新しい行にISBNを保存すると、それをトリガーにApps Scriptを実行し書籍の情報を追記します。

スプレッドシートの上部メニュー「拡張機能」から「AppSheet > Create an app」を選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/2f34bdd6-5c46-2e12-d3da-1259a1729b9b.png)

少し待つと、AppSheetのアプリが出来上がります。`Customize your app`ボタンを押して、先に進みましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/eb2219fb-9752-5f59-85fe-ef9baef1e701.png)

ここから、Data（データ）とView（ビュー）を調整していきます。

## Data > Books

左ペインDataを選択し、Booksを選択します。

ISBNは、REQUIRE?（必須）とSCAN?（バーコード）をチェックします。

| NAME | TYPE | REQUIRE? | INITIAL VALUE | SCAN? |
|---|---|---|---|---|
| _RowNumber | Number | | |
| ISBN | **Text**（変更）| ✔ | | ✔（変更）　|
| title | TExt | | | |
| subtitle | Text | | | |
| authors | Text | | | |
| publisher | Text | | | |
| publisedDate | **Date**（変更） | **外す**（変更）| **空にする**（変更）| |
| description | **LongText**（変更） | | | |
| thumbnail | **Thumbnail**（変更） | **外す**（変更） | |
| createdAt | **DateTime** | **外す**（変更）| **=NOW()**（変更） | |

保存すると、右側の画面プレビューに書影のサムネイルが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/8375a068-ef08-be1d-7630-90bb45265efd.png)

## Views > Books

ここからはViewの調整です。左ペインのスマホアイコンでViewsに切り替えて、`PRIMARY NAVIGATION`から`Books`を選びます。

View typeは`deck`にします。

View Optionsは次の通りです。

- Sort by
  - publishedDateになっているのを削除
- Main Image
  - **thumbnail**
- Primary header
  - **title**
- Secondary header
  - **authors**
- Summary column
  - **publishedDate**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/b329e74e-2b64-819a-4d18-985033899a5c.png)

## Automation

ここからは、ISBNが追加されるとApps Scriptを実行するように設定していきます。左ペインのロボットアイコンを押します。`Create my first automation`ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/21bddcf0-1567-c924-da22-aafead0a972d.png)

`Create a new bot`ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/cafb078c-2af6-38b4-4899-c8825e2871f9.png)

ここからは`EVENT`と`PROCESS`をそれぞれ設定します。EVENTはこのBotを実行するトリガーになるイベントを設定し、PROCESSは実行される処理を設定します。`Configure event`ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/d9d4d1f6-b28e-4069-343c-0a42ea65798d.png)

Create a custom eventを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/2733e969-6f90-a6f9-9564-c6bf083df59f.png)

右側の画面プレビューが設定画面に切り替わります。切り替わらない場合はギアアイコンを押してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/b194c917-fd55-8244-5517-3f8c3854388b.png)

EVENTは次のように設定します。

- Event Type
  - Data Change
  - **Adds only**
- Table
  - **ISBN**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/d87339f5-796f-4cd4-2bc3-1c29d3a9f65c.png)

今度は、画面真ん中でAdd a stepボタンを押してPROCESSを設定します。`Create a custom step`を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/18a341a7-c1bc-246d-4658-afb669a3335f.png)

PROCESSは次のように設定します。

- Settings
  - **Call a script**
- Table name
  - **ISBN**
- Apps Script Project
  - **（作成したApps Script）**
- Function Name
  - **updateBook(isbn)**
- Function Parameters
  - **=[ISBN]**
- Return Value
  - チェックなし
- Run asynchronously
  - チェックなし

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/57983985-67a4-f771-2f4a-cbaed73c0652.png)

なお、Apps Script Projectを選ぶときに、ファイルを選択するように言われます。ここで、単独のApps Scriptを選択しないとエラーになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/cf257cbe-2ca0-6c68-8327-6ae04b5a6d05.png)

## 動作確認

お疲れ様でした！これで設定は完了です。忘れずに画面右上のSAVEボタンを押しましょう。

動作を確認してみます。画面右側のプレビューでスマホアイコンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/386fb9f3-9dd1-12fb-d537-c5a51fdd06cd.png)

プラスアイコンを押して、新しい本を追加します。ISBNに`4774142042`を入力して、Saveボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/38985244-dc34-078c-6171-6a9e18e99306.png)

次のように表示されればOKです。もしうまく行かない場合は、画面右上のSAVEボタンを押したか確認してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/ae7b42e8-5302-950e-87e6-05ea4fcb366c.png)

もし期待通りに動作しない場合は、Automation Monitorを確認してみてください。何かヒントが得られるかもしれません。左ペインの下から2番目Manageアイコン > Monitor > Automation Monitor > Lauch automation monitor で起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/f251286e-a858-d43f-44a5-f20c418dd3cf.png)

画面下部Runsを選択すると、Automationが実行された様子を確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/b07e8934-b8f4-2426-a876-8a04921238ed.png)

# まとめ

Googleスプレッドシート、Google Apps Script、AppSheetを使って、無料の蔵書管理アプリを作ってみました。AppSheetからスタンドアロンのApps Scriptを呼び出せるようになったおかげで、より高度なアプリを作れるようになりました。

もし、バーコードを連続して読み取りたい場合は次のスクリプトを参考にしてください。AppSheetを使わずApps Scriptオンリーで作っていきますが、こちらも無料で実現できます。

https://qiita.com/takatama/items/5ef6b31c4f228f0393e1