JavaScriptだけで定期的にデータを更新してくれるお洒落ダッシュボードを作る（no code編もあるよ！）

# はじめに
データ分析って面白いけど、データ集めるのって大変ですよね。
ダッシュボードって見やすいけど作るの大変ですよね。
ということで、JavaScriptだけでデータを自動更新してくれるちょっと格好いいダッシュボードを作ってみます。

最初に謝っておきたいのは、ちょっとタイトル詐欺をしてしまっていて、完全なダッシュボードはここでは作りません。グラフの表示までで、それをどう並べるかはお任せになっています。。。

一応完成版イメージは載せますが、どのようなグラフ、配置をするかは各々の方法でお願いいたします。

タイトル通りJavaScriptだけで作るパターンと（ほぼ）no codeで作るパターンの2種類ありますが、JavaScriptだけで作る場合は基本的に無料で、no codeの場合は有料です（料金はあとで説明します）。

no codeは一応おまけなので最後にチラッと説明しますが、おすすめはこちらです。（有料ですがほぼno codeでしかも爆速）

# 完成イメージ
<img width="1431" alt="Screen Shot 2020-07-13 at 10.12.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/901f0978-36c6-8141-4c1b-d757a3265ba3.png">


# 選定技術
以下の技術を用いて作ってみます。
- 言語：JavaScript
- ツール：BigQuery、Cloud Functions

ただし、BigqueryやCloud Functionsはたくさん使うと有料になってしまうので気をつけてください。

# Cloud FunctinosでBigQueryからデータを取得

### ファイルの準備
Cloud Functionsを使ってBigQueryにあるデータにアクセスできるようにします。

まずはCloud Functionsを使える状態にします。

適当なディレクトリでCloud Functionsを導入します。
導入方法は調べると色々出てくると思うので割愛させて頂きます。

そうするとこんな感じのディレクトリ構成になると思います

your_dir
 |-index.js
 |-package.json
 |-package-lock.json
 |-node_modules


ここにBigqueryに接続するための接続キーが必要なので、[こちら](https://cloud.google.com/bigquery/docs/reference/libraries)を参考に作成してください。（jsonで）

そのjsonファイルをyour_dir内に設置してください。
このjsonファイルの名前を仮にkey.jsonとしておきます。

### 関数の作成

bigqueryのライブラリを使用したいので次のコマンドを実行します。

`npm install --save @google-cloud/bigquery`

次にindex.jsを開いて次のように編集します。

```javascript:index.js
const { BigQuery } = require("@google-cloud/bigquery");

const bigquery = new BigQuery({
  projectId: "作成したプロジェクトのID",
  keyFilename: "key.json", //ここでキーの場所を指定する
});

// BigQueryに問い合わせるクエリを記述
// ここでは7月の東京駅の気温を取得している
const query = "SELECT da, temp  FROM `bigquery-public-data.noaa_gsod.gsod2020` WHERE stn = "476620" and year = "2020" and mo = "07" ORDER BY da";

exports.bigquery = (req, res) => {
  //ヘッダーにこれらを入れないと後々作成するJavaScriptから呼び出せない
  res.header("Access-Control-Allow-Origin", "*");
  res.header(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept"
  );
  bigquery
    .query(query)
    .then((data) => {
      const rows = data[0];
      res.send(rows);
    })
    .catch((error) => {
      console.log(error);
    });
};

```

ここでBigQueryが公開してるパブリックデータから東京駅の気温（華氏）を取得しています。

このパブリックデータは定期的に更新されるのでアクセスの度に最新版が取得されます。

**※このクエリでBigQuery上で数十MB処理されます。アクセスする度にそれだけ処理されてたくさんアクセスすると無料枠をはみ出る恐れがあるので注意してください。**

必要があれば自分で作ったテーブルにアクセスすることで処理量を減らしたり、定期的に行われるバッチ処理を作成してDBにデータを入れる等して処理量が無料枠に収まるようにしてください。

ここでは割愛しますが、BigQueryには定期的にテーブルを更新する（スケジュール）機能があるのでそちらを使うと処理量は減ると思います。


### Cloud Functionsデプロイ

作った関数をデプロイします。
デプロイの方法は色々あるのですが、GCPのストレージに保存する方法を用います。

作った関数をStorageにアップロードしますのですが、まずアップロードするファイルを作成します。

作ったファイル類をzipファイルに圧縮します。

ここで気をつけたいのが、圧縮するのはファイル類です。
なので、以下のフォルダではなく、

<img width="111" alt="Screen Shot 2020-07-11 at 20.56.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/4a2d8bbb-1b05-07bf-545a-b4e3876e4f8c.png">

こっちのファイル類をまとめて圧縮してください。（実際はjsonのキーファイルも一緒にまとめて圧縮）

<img width="454" alt="Screen Shot 2020-07-11 at 20.56.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/ef367ac6-ff4f-20c4-ee00-78e2a1495c8f.png">

そうしてできたzipファイルをストレージに保存します。

GCPの左のタブからStorage=>ブラウザを選びます。

<img width="568" alt="Screen Shot 2020-07-11 at 20.59.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/fb34218b-bc6c-eda4-96cb-d2e62e90240e.png">

その中でバケットを作成し、先ほど作ったzipファイルをアップロードします。

アップロードが完了したら、ストレージと同様にGCPの左のタブからCloud Functionsを選びます。

名前をテキトーにつけて、トリガーはHTTPにします。

<img width="693" alt="Screen Shot 2020-07-11 at 21.02.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/5118a66b-5fb9-738f-45e0-2795679a0802.png">

デプロイ元は先ほど保存したzipファイルを指定します。
<img width="1349" alt="Screen Shot 2020-07-11 at 21.03.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/25834613-83e5-d384-3c74-7dcabf41e0d4.png">

ランタイムはNode.js 10、実行する関数は先ほど作成したindex.jsで作ったbigqueryという関数です。

これでデプロイ完了です。

デプロイした関数を実行するURLにアクセスしてください。こんな感じで無事データが返ってきてるかと思います。

`[{"da":"01","temp":75.5},{"da":"02","temp":79.5},{"da":"03","temp":73.1},{"da":"04","temp":77},{"da":"05","temp":76.4},{"da":"06","temp":78.3},{"da":"07","temp":81.1},{"da":"08","temp":78.9}]`

日にちとその日にちでの気温（華氏）ですね。

# ダッシュボードの作成

次は取得したデータを使ってグラフを作成します。
グラフは**chart.js**を使います。

先ほどアクセスしたURLで取得できるjsonデータをchart.jsで読み込める形式に変換します。

```html:index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>グラフ</title>
  </head>
  <body>
    <h1>棒グラフ</h1>
    <canvas id="myBarChart"></canvas>
    <script
      src="https://code.jquery.com/jquery-3.5.1.js"
      integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc="
      crossorigin="anonymous"
    ></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js"></script>
    <script src="./main.js"></script>
  </body>
</html>

```

```javascript:main.js

(function () {
  window.onload = function () {
    const url = "先ほど作成したcloud functionsのURL";

    const ctx = document.getElementById("myBarChart").getContext("2d");

    $.getJSON(url, function () {
      console.log("success");
    })
      .done(function (data) {
        let day = [];
        let temp = [];
        data.forEach((element) => {
          day.push(Number(element["da"]));
          // 摂氏に変換
          temp.push(((Number(element["temp"]) - 32) * 5) / 9);
        });

        const chart = new Chart(ctx, {
          type: "bar",
          data: {
            labels: day,
            datasets: [
              {
                label: "東京駅気温",
                data: temp,
                backgroundColor: "rgba(219,39,91,0.5)",
              },
            ],
          },
          options: {
            scales: {
              xAxes: [
                {
                  ticks: {
                    autoSkip: false,
                  },
                },
              ],
              yAxes: [
                {
                  ticks: {
                    suggestedMax: 30,
                    suggestedMin: 0,
                    stepSize: 1,
                    // 縦軸に単位を追加
                    callback: function (value, index, values) {
                      return value + "℃";
                    },
                  },
                },
              ],
            },
          },
        });
      })
      .fail(function () {
        console.log("error");
      })
      .always(function () {
        console.log("complete");
      });

  };
})();

```

これで下のようなグラフが得られると思います。

<img width="1429" alt="Screen Shot 2020-07-11 at 22.41.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/054367b6-a0b5-2590-33d0-9f7a844e7907.png">


ダッシュボード自体はboot strap等のテンプレートを使えば簡単にそれっぽくできます。
今回は[こちら](https://github.com/StartBootstrap/startbootstrap-sb-admin-2)を使いました。
それっぽいグラフをいくつか作り、データをのっければ無事ダッシュボードの完成です！

<img width="1431" alt="Screen Shot 2020-07-13 at 10.12.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/901f0978-36c6-8141-4c1b-d757a3265ba3.png">




# おまけ
ここからはほぼno codeで作るダッシュボードです（コードはSQLのみ）。
有名BIツールであるTableauとGoogle Sheetを使用すると爆速でダッシュボードができます。

Tableau有料版を使うともっと簡単なのですが、なにせ非常に高額（年間10万円くらい）なので、Tableau Publicを使用します。

Google Sheetと連携させてデータを取得するのですが、Google SheetとBigQueryの連携にG Suiteの契約が必要になってきます（Enterprise版もしくはEssentials）。Essentialsの方は月10ドルなので割とお手頃かなと思います。今回はEnterprise版で行ったのでEssentialsの方での動作確認はしてません（ドキュメントに書いてあるからおそらく大丈夫だとは思いますが）。

### BigQueryとGoogle Sheetsの連携

[こちら](https://qiita.com/yskur/items/a5a7984b0e9b6d65dfb3)の記事に全てが載っています。ありがたい。

これで定期的にGoogle Sheetsの内容を更新できます。

先ほどと同じようなクエリを作って、データを取得します。

<img width="704" alt="Screen Shot 2020-07-12 at 16.53.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/d6a36964-cc7e-de00-570d-2dec2eb6f7b6.png">

このクエリを投げると60MBくらい処理するということです。
1TB/月までBigQueryは無料（少なくとも今は）なので、1日1回くらいなら全く問題ないですね。

結果は下記のようになります。同じクエリを投げてますが、クエリを投げた時間が違うのでda=09のレコードが増えてます。

<img width="322" alt="Screen Shot 2020-07-12 at 16.54.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/68457080-1be9-bd48-7cfb-7c5f4888dbb6.png">

###TableauとGoogle Sheetsの連携

Google Sheetsで作成したデータをTableau Publicと連携させます。最初に開いた画面の左側のタブのTo a ServerのGoogle Sheetsを選択します。

<img width="243" alt="Screen Shot 2020-07-12 at 17.36.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/412e29bb-da4f-99a1-842c-8ab0ce31e90e.png">

接続確認を終了させると、Google Sheetsからデータを取得します。
そのデータから簡単に下のような図が作れます。

![Screen Shot 2020-07-12 at 17.22.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/62271760-7945-f030-03eb-62cc2b1b5f36.png)

**※Tableau Public、つまり無料版だとGoogle Sheetsのデータの反映は1日1回らしいので、頻繁に更新されるデータを即時反映させる場合にはTableauの有料版を使用する必要があります。**

Tableauの使い方自体はここでは説明しませんが、色々なグラフを追加することで簡単にダッシュボードが作成可能です。

[こちら](https://public.tableau.com/en-us/gallery/?tab=featured&type=featured)で色々な格好良いダッシュボードが見られます。

ここで表示されてるのは本当に手間がかかっていてすごいんですが、例えばこのリンクの中の検索ボックスで「売り上げ分析」と検索して出てくるようなダッシュボードは割と簡単に作れます（もちろんものによりますが）。

ただ、Tableau Publicはデータが公開されてしまうので、外部に公開しても良いデータでないと使えないのが難点ですね。






# まとめ
一応言語は一つ縛りだったので、JavaScriptしか使いませんでした。
（Node.jsもjsファイルだし、クエリもjsファイル内で書いてるし、htmlはプログラミング言語じゃないし、書こうと思えば全部JSで書けるから、これはもうJSしか使ってないと言っても過言ではないはず！）


お金があればno codeの方でやっちゃうのもありかな。