【PowerBI】Notionのデータをグラフで可視化！データの自動更新もあるよ

# はじめに

Notionを使い始めて1年くらいが経ち、データベースにいろいろなデータが蓄積されてきました。ただ、Notion単体ではデータのグラフ化の方法が見当たらなかったため、PowerBIと連携してデータの可視化を行おうと考えました。Notionにはcsv出力の機能がついているので、データベースを手動でcsv出力すれば簡単にデータを取得することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2916360/4dd84429-4f01-faf5-8c1f-ebdb55b6527c.png)

しかし、データの更新頻度が日毎や週毎のものになるとそのたびにデータの出力を行う必要があるため、**NotionとPowerBIを連携させてデータを自動的に取り込む仕組み**を作ってみました。

# 手法

[NotionとDomoをGoogleスプレッドシートでデータ連携する方法をご紹介：【連載】Notionでタスク管理＆モチベーションアップ！第3回](https://note.atara.co.jp/n/nba11991b2b13)を参考に、**NotionAPIを用いてGoogleスプレッドシートにデータを出力してPowerBIへ読み込ませる**という手法を取りました。

:::note info
Notion > Googleスプレッドシート > PowerBI
:::

# Notion > Googleスプレッドシート

### Notion側の出力設定

NotionAPIを用いてデータベースの外部出力を行います。
[参考サイト](https://note.atara.co.jp/n/nba11991b2b13)の「1. Notionでの事前設定」項を基に設定を行いましたが、具体的に行うことは主に以下の3つです。

- インテグレーションを作成し、Notionデータベースの接続先として設定
- APIキーの取得
- データベースIDの取得

これらを行うことで、Googleスプレッドシート側でデータベースの中身を受け取るための準備が完了します。

### Googleスプレッドシート側の入力設定

Googleスプレッドシート側で入力を受け取るために、AppsScriptを用います。
[参考サイト](https://note.atara.co.jp/n/nba11991b2b13)の「3. GASでNotion APIを実行するコード作成」のサンプルコードを参考に、以下の点に変更を加えました。

- 固有値
    - APIキー
    - データベースID
    - スプレッドシートのシート名(ファイル名ではないことに注意)
- カラムの読み込み・書き込み
    - 読み込んだデータの配列格納
        - カラム名
        - データのプロパティ型に応じた属性の設定
    - 書き込み

自分が実際に修正を加えた後のコードは以下です。(データベースIDとAPIキーは置き換えてあります。)
変更を加えた点にのみコメントをしてあるので、そこを中心に見ると分かりやすいかと思います。

<details>
<summary>コード変更例</summary>

使用したデータベース
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2916360/89a8593a-610e-930e-8002-0f230d1a5c3b.png)

``` plane_text:サンプルコード修正後

function getNotionDbData(start_cursor_arg, page_size_arg) {
  const url = 'https://api.notion.com/v1/databases/hogehoge/query'; // データベースIDを記載
  const token = 'secret_hogehoge'; // APIキーを記載
 
  let headers = {
    'content-type' : 'application/json; charset=UTF-8',
    'Authorization': 'Bearer ' + token,
    'Notion-Version': '2021-08-16',   
  }
  
 
  let pre_payload = {
    'page_size' : page_size_arg,
    'start_cursor' : start_cursor_arg, 
  }
 
  let options ={
    'method': 'post',
    'headers': headers,
    'payload': JSON.stringify(pre_payload),
  }
 
 
  let notion_data = UrlFetchApp.fetch(url, options);
  notion_data = JSON.parse(notion_data);
 
  return notion_data;
}
 
 
function writeSpreadSheet(sheet_name, target_array, loop_num, colmn){
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheet_name);
  let row = 2 + loop_num * 100;
  
    target_array.forEach((value) => {
      sheet.getRange(row, colmn).setValue(value);
      row = row + 1;
  });
}
 
 
function resetSpreadSheet(sheet_name){
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheet_name);
  const lastRow = sheet.getLastRow();
  const lastCol = sheet.getLastColumn();
 
    for(let col=1; col<=lastCol; col++){
      for(let row=2; row<=lastRow; row++){
        sheet.getRange(row, col).setValue("");
      }
    }
 
}


function notionDataToSpreadSheet(){
  const sheet_name = 'Data';
 
  let db_data=1;
  let datas;
  let ids;
  let id;
  let loop_num = 0;
 
  let names;
  let name;
  let genres;
  let genre;
  let evaluations;
  let evaluation; 
 
  resetSpreadSheet(sheet_name)
 
  do{
    if(db_data == 1){
      nx_cursor = undefined;
    }else{
      nx_cursor = db_data.next_cursor;
    }
 
    db_data = getNotionDbData(undefined, 100);
    datas = db_data.results;
 
    ids = [];
    names = [];
    genres = [];
    evaluations = [];
 
    datas.forEach((database)=>{
      id = database.id;
      ids.push(id);
    
 
      //Notionデータベースの「タイトル」プロパティの「名前」を格納
      name = database.properties.名前.title[0];
      if(name === null || name === void 0)
        {names.push("")}
      else
        {names.push(name.text.content)}
      ;
 
 
      //Notionデータベースの「セレクト」プロパティの「ジャンル」を格納
      genre = database.properties.ジャンル.select;
      if(genre === null)
        {genres.push("")}
      else
        {genres.push(genre.name)}
      ;
 
 
      //Notionデータベースの「セレクト」プロパティの「オススメ度」を格納
      evaluation = database.properties.オススメ度.select;
      if(evaluation === null)
        {evaluations.push("")}
      else
        {evaluations.push(evaluation.name)};
    }
    )
 
    //スプレッドシートに書き込む列の数だけ追加する
    writeSpreadSheet(sheet_name, names, loop_num, 1);
    writeSpreadSheet(sheet_name, genres, loop_num, 2);
    writeSpreadSheet(sheet_name, evaluations, loop_num, 3);
 
    loop_num++;
  }
  while(db_data.has_more == true);
 
}
```

</details>

### トリガーの設定
トリガーの編集を行って"notionDataToSpreadSheet"関数を任意のタイミングで呼び出すことによりデータの更新が行えるようになります。
今回は運用テストを行いたかっただけなのでスプレッドシート起動時に更新が行われるようにしましたが、毎日自動更新したい場合はトリガーを日毎の時間指定にすればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2916360/1bfc2912-cb2e-4174-6e81-eea09c5c0b21.png)


# Googleスプレッドシート > PowerBI

### データの読み込み

PowerBIDesktopを開き、「データを取得>詳細...>すべて>Google スプレッドシート」 とするとgoogle スプレッドシートのURLを求められるので、読み込みたいシートのURLを貼り付けます。

そうすると以下の画像のような画面が出てくるので、「Data」のチェックボックスを埋めた後に「読み込み」ボタンを押すことでデータの読み込みが完了します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2916360/a71cc928-3e21-523d-2e4f-46bd14565440.png)

### データの更新

PowerBIDesktopでデータの更新を行うには上部の「更新」ボタンを押す必要があります。普段使いするにはワンボタンなので気にならないと思いますが、読み込むデータの容量が多い場合はボタンを押した後に時間がかかります。その場合はPowerBI Serviceに発行することで自動更新を実現できますが、今回は割愛させていただきます。

# 実用例

読み込みができたので後はPowerBIを使って自由にデータを可視化できます。
簡単な例を示しておくと、以下の画像は自分が良く見る映画のジャンル傾向をグラフに示したものです。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2916360/de6e2788-1a65-83b8-64f1-abaeee6ebbce.png)

他にもデータ更新の自動化によって以下のような活用法が考えられます。

- 各メンバーの工数を可視化して週次報告に用いる
- 日々の摂取カロリーなどを可視化して健康状態を把握する
- 毎日の出費を可視化してより分析された家計簿を作る
- ...etc

# まとめ

元々は、「会社でタスクの工数をNotionに記載してそれを基に報告を行っているが、これをグラフで見せることによって**どこにどれだけ時間を使ったかというのを視覚的に表せる**のではないか？」と思ったのが始まりでした。
今回作った仕組みを使えばデータの更新の手間がかからないため、長く運用してもらえる環境を作れるのかなと思います。
一方でGoogleスプレッドシートに出力する関係上、一旦クラウド上にデータを上げる必要があるのでセキュリティ面で気をつける部分が増えるのはマイナスかもしれません。


PowerBIと連携して、より良いNotionライフを！

# 参考サイト

https://note.atara.co.jp/n/nba11991b2b13