
[Notion]チャート機能を使ってnote月次データを簡易ダッシュボード化した
ここ数日色々ガチャガチャ触っていたのですが、色々ガチャガチャ触ると知らんかったところにハマったりしてやはり学びがありますね。紆余曲折した結果、DLしたCSVデータをnotionにインポートしてチャートで見ただけになった。
やりたかったこと
noteの記事データをnotionに自動転記したい
noteの売上をうまいこと見たい
noteの日足売上データをnotionに自動転記したい
notionのチャート機能使いたい
やってみた結果
noteの記事データをnotionに自動転記したい 〇
noteの売上をうまいこと見たい △
noteの日足売上データをnotionに自動転記したい △
notionのチャート機能使いたい ◎
って感じです。
100点ではないけどまぁ及第点かな~~~くらいの設計はできたのですが、やってみてつくづく思ったのは「note、データ取るのめちゃ面倒ですよね」っていうことです。頼む~~~、ポチっとしたら一覧をDLするみたいな設計と実装を~~~~頼む~~~~~。
noteの記事データをnotionに自動転記したい 〇
記事データ(タイトル、投稿日時、URL、価格)の収集と転記はできた
ただし、noteの全量データ(タイトル 投稿日時 URL)を取るscriptまでやりきれず、RSSで25件のみ引っ張ってくる実装にした(RSS 25件はnote側の制約)
noteの売上をうまいこと見たい 〇
月次データ単位ならば可能
日次データ単位も見れないことはないが要改修
noteの日足売上データをnotionに自動転記したい △
スプシに集約しているデータをnotionに転記…というscriptは実装できた
既存データが多すぎて転送だとラグで数値データの欠損や重複がありそう(行数が合わない)だったので、増分データ転送ならいけそう
notionのチャート機能使いたい ◎
使った
Notionチャートで簡易ダッシュボードできた
ビジュがいいね~って感じ。
2軸データ見る分にいは十分かなって感じがあります。

スプシ > Notion 転記 GAS
◆処理
googleスプシに格納されたデータをnotion DBに登録
登録された行にはスプシJ列にdoneフラグを付与
スケジューラーで15分ごとに実施
<スプシ>

<Notion>

function addDataToNotionFromSpreadsheet() {
const props = PropertiesService.getScriptProperties();
const dbId = props.getProperty('NOTION_DB_ID'); // NotionデータベースID
const token = props.getProperty('NOTION_TOKEN'); // Notion APIトークン
const apiUrl = 'https://api.notion.com/v1/pages';
const sheetName = 'input_folder'; // スプレッドシート名
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
if (!sheet) {
Logger.log(`シート "${sheetName}" が見つかりません。`);
return;
}
const lastRow = sheet.getLastRow();
if (lastRow < 2) {
Logger.log("データがありません。スクリプトを終了します。");
return;
}
for (let i = 2; i <= lastRow; i++) {
const isTransferred = sheet.getRange(i, 10).getValue(); // 転記済列 (J列: 10列目)
if (!isTransferred) {
const fullDateTime = String(sheet.getRange(i, 1).getValue()).trim(); // 決済日 (A列)
const yearMonth = String(sheet.getRange(i, 2).getValue()).trim(); // 年月 (B列)
const date = String(sheet.getRange(i, 3).getValue()).trim(); // 年月日 (C列)
const time = String(sheet.getRange(i, 4).getValue()).trim(); // 時刻 (D列)
const weekday = String(sheet.getRange(i, 5).getValue()).trim(); // 曜日 (E列)
const purchaserName = String(sheet.getRange(i, 6).getValue()).trim(); // 購入者名 (F列)
const contentType = String(sheet.getRange(i, 7).getValue()).trim(); // コンテンツ種別 (G列)
const contentName = String(sheet.getRange(i, 8).getValue()).trim(); // コンテンツ名 (H列)
const price = sheet.getRange(i, 9).getValue(); // 価格 (I列)
const pageObj = {
parent: {
database_id: dbId,
},
properties: {
"決済日": {
"title": [{ "text": { "content": fullDateTime } }] // タイトル型(決済日)
},
"年月": {
"select": { "name": yearMonth } // セレクト型(年月)
},
"年月日": {
"rich_text": [{ "text": { "content": date } }] // テキスト型(年月日)
},
"時刻": {
"rich_text": [{ "text": { "content": time } }] // テキスト型(時刻)
},
"曜日": {
"rich_text": [{ "text": { "content": weekday } }] // テキスト型(曜日)
},
"コンテンツ名": {
"select": { "name": contentName } // セレクト型(コンテンツ名)
},
"コンテンツ種別": {
"select": { "name": contentType } // セレクト型(コンテンツ種別)
},
"価格": {
"number": Number(price) // 数値型(価格)
},
"購入者名": {
"rich_text": [{ "text": { "content": purchaserName } }] // テキスト型(購入者名)
}
}
};
Logger.log(`送信データ: ${JSON.stringify(pageObj)}`);
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`,
"Notion-Version": "2022-06-28",
},
payload: JSON.stringify(pageObj),
muteHttpExceptions: true,
};
try {
const response = UrlFetchApp.fetch(apiUrl, options);
const responseCode = response.getResponseCode();
const responseText = response.getContentText();
if (responseCode === 200) {
Logger.log(`行 ${i}: Notionにデータを転記しました。`);
sheet.getRange(i, 10).setValue("done"); // 転記済みフラグをJ列に設定
} else {
Logger.log(`行 ${i}: 転記失敗 - ステータス: ${responseCode}, レスポンス: ${responseText}`);
}
} catch (e) {
Logger.log(`行 ${i}: エラーが発生しました - ${e.message}`);
}
}
}
}
上記自体は動いてたんですが、1万行くらいのデータ転送しようとしたらどうも数値が合わない…?ということが発生しました。
結局CSVをnotionにインポートするが…
データ量多いしね…ってなって、そのままCSVインポートして増分転送にするか~~~と思って、CSVインポートでNotion DB作ろうとしたところ、タイトル(Notionの主キーっぽいとこ、主キーではないのだが…)に決済日データ[20231001003140]が入らない…!なぜか時刻がタイトルにされる!という事象が発生しました。
GASで転送していた時は決済日データ[20231001003140]が入ってたし、当然手うちで入力するときも[20231001003140]は入るにも関わらず…なに!??!?
で、軽く調べたら「NotionのCSVインポート時は基本的に左端列をタイトルに入れるが、数値のみデータであるとCSVインポート時はタイトル設定せず、次に現れた数値のみデータではない列をタイトルとして設定する」という仕様のようです。何その仕様…。この時だと時刻データ[hh:mm:dd]がそれにあたった…ということのようです。
さすがに時刻データで登録されていくの気持ち悪い…となり、決済日データ&購入ID列をつくり、それで登録しました。

数値、行数などは当然一致していたので、上記をベースに増分をGAS転送にするか~~~などと今考えています。
月次データは見れるけど…
結局今回はnoteからDLした販売データをインポート…という手動処理のままになっちゃいました。が、まぁ月次データのインポートくらいなら数値ずれるより普通に手動でやったほうがよくない???という身も蓋もない話はある。
note からファイルDL > gdriveに格納 > GAS で転送 > notion側で閲覧
自動化するならこう…って感じだけど、月次データの月1処理をここまで自動化する理由はないのよね…という「手数と楽さのバーター」の分水嶺を実感したりしました。
メール通知でとってるリアルタイム販売データをnotion側に流し込むことは可能なんだけど、そうまでしてリアルタイムデータ見たい???というのもちょっと微妙だな~って話もあり、一旦考えよ…となります。というか、だったらnotionじゃなくて、google側のダッシュボードにしたほうが絶対いいし。

一度フロー整理しないとわけわからんくなるか~~~という感じです。
ここから先は
ありがとうございます。『あなたの課金は、私の課金』を標語に経済をまわしていきましょう。