見出し画像

GAS初心者がChatGPTと作るツール開発記録① 【Google Calendar×Notion連携】

こんにちは、ひろみんです。

このシリーズは、プログラミング初心者がGASを使ってツール開発を行う過程の試行錯誤の記録を綴ったものです。
私は2024年12月現在、以下のようなスペックです。

・HTMLとCSSは使える
・JavaScriptやGoogle Apps Script(GAS)については全くの素人
・「GASの開発画面ってどうやって立ち上げるんですか?」という状態

中小企業の業務効率化を行う上で、「人間がしなくていい作業は自動化」が私のモットーです。
今回、私が提供する中小企業に向けたフルオーダー業務改善ツール「ビズカスタム」を導入いただくお客様に向けてより良いツールをご提供するために、GASを使ったツール連携の必要性を強く感じ、ChatGPTをパートナーとして開発に取り組むことにしました。

この記事では、プログラミング初心者がAIの助けを借りながら、実用的なツールを作り上げていく過程をで得られた経験と学びを共有していきます。
結論から言うと、私のような開発初心者でも、ChatGPTと対話しながら少しずつ理解を深め実働に足るツールを開発することができました

開発したツールのデモストレーションはこちらです。

この経験が、同じように業務改善に悩む方々の参考になることを願っています。



はじめに

今回は、個人サロンの業務改善に役立てるツールを作成します。
個人サロンといっても様々なジャンルがありますが、今回はこれから独立しようと準備している個人サロンがターゲットです。

具体的に以下のような種類のサロンのスタートアップに導入することを想定しています。

・エステサロン
・ネイルサロン
・マッサージ店
・まつげサロン
・鍼灸院

これらのサロンの特徴として、予約制で運営され、顧客一人一人に対して丁寧なカウンセリングとケアを提供することが挙げられます。


開発の背景と目的

個人サロン業界では、予約管理、顧客管理、会計など、それぞれの目的に特化した便利なツールが数多く存在します。しかし、これらのツールは互いに連携していないことが多く、スタッフは必要な情報を得るたびに異なるツールの画面を開いて参照する必要があり、業務の流れが分断される状況が生じていました。

課題設定

個人サロンにおいて今回取り上げる課題は以下の2つです。

・予約管理の非効率性: 既存の予定と新規予約の管理に多くの時間と労力を要しており、より効率的な予約システムの検討が必要
・カルテ管理の整備: お客様のカルテ情報を体系的に管理・整理し、スタッフ間で効率的に共有できる仕組みが必要

これらの課題を解決するため、予約管理システムとカルテ管理システムの連携が不可欠でした。

選定ツール

サロンの予約管理を行うにあたり、個人サロンのオーナーが日常的に使用しているGoogle Calendarと連携し、お客様が直接予約可能な仕組みを構築したいという要望がありました。そこで選定したツールがGoogle Calendarの予約調整機能です。

・無料で利用可能:日々のカレンダー運用を工夫する必要あり
・比較的安価で付帯機能が便利:課金する場合でも月々1,300円で、さらに2TBのGoogleドライブ要領がついてくる

Google Calendarの予約調整機能の特徴

このように、比較的リーズナブルに導入できる点と、月額課金して2TBのストレージがついてくる点において、コストパフォーマンスに優れているといえます。これにより、オーナーの予定管理とお客様の予約プロセスを一元化し、より効率的な予約システムの実現を目指しました。

さらに、カルテの編集と管理にはNotionを選定しています。

・情報の一元管理が可能:データベース機能を活用することで、スタッフ情報やお客様情報とカルテをシームレスに繋ぎ、一元管理することができる
・機能拡張が容易
・Googleカレンダーとの同期が可能:NotionAPIを活用

Notion選定理由

これまでは予約が入るたびにカルテへ手動で情報を転記する必要がありましたが、この作業の自動化で業務効率の向上を目指しました。

目指した機能

  • Google Calendarに新規予約が入った際、自動でNotionデータベースに顧客情報を作成する

  • 連携する情報:

    • イベント名(titleへ)

    • 予約の開始時間(来店日の開始時刻へ)

    • 予約の終了時間(来店日の終了時刻へ)

Notionデータベースのカルテ情報

完成した機能のデモ

以下に、実際に機能が動作した際の流れを示します。

  1. Google Calendarに新規予約が入った場合

    • 予約内容:「予約(山田花子)」

    • 予約日時:2024年12月25日 14:00-15:00

  2. システムが新規予約を検知

    • 前回チェック時刻:2024-12-19 11:10:00

    • 現在のチェック時刻:2024-12-19 11:15:00

    • → この間に作成された予約を検出

  3. Notionデータベースに新規エントリーが自動作成

    1. title 来店日時 予約(山田花子) 024-12-25 14:00-15:00

このように、予約情報が自動的にNotionデータベースに反映され、手動での情報転記が不要になりました。


ChatGPTとの対話を通じた開発へ

最初のアプローチと課題

当初、Google Calendarの新規イベントをトリガーとしてNotionにデータを送信する実装を目指しました。そこでChatGPTに「Google Calendarの新規イベントをトリガーにしてNotionにデータを送信するコードを作成したい」と相談したのですが、開発を進めるにつれて次の課題に直面しました。

  1. イベントトリガーでのタイトル取得エラー

    • イベント追加時に「イベントのタイトルを取得できない」エラーが発生

    • 知識不足が原因で、エラーの原因特定と解決に予想以上の時間を要する

  2. 実装の複雑さ

    • 全体を一度に実装しようとしたため、エラーの原因特定が困難に

    • デバッグを進める中で、段階的なアプローチの必要性を認識

方針の見直し

これらの課題から、以下のように開発方針を見直しました。

  1. トリガーを時間式に変更

    • 定期的な時間間隔でイベントをチェックする方式に変更

    • 5分おきに新規予約の有無を確認するように設定

    • イベントトリガーの複雑な処理を回避し、より安定した動作を実現

  2. 機能を分割して段階的に実装

    • Step 1: NotionへのデータベースAPI連携の実装と動作確認

    • Step 2: Google Calendarの新規イベント検知機能の実装と動作確認

    • Step 3: 両機能の統合と全体テスト

  3. 各段階でのエラー対応と改善

    • 個別の機能ごとにデバッグを実施

    • エラーの原因特定と修正を効率的に実施

これの開発方針の変更によって、以下のようなメリットが得られました。

  • 各機能の動作確認が容易に

  • 問題発生時の原因特定がしやすい

  • 必要に応じて機能の修正・改善が可能


具体的な開発内容解説

この章では、実際のコードと一緒に、開発手順を解説します。

Notionへのデータ送信機能の実装

まず、Notionデータベースに新規ページを追加するコードを生成します。


  const notionApiKey = "YOUR_API_KEY";
  const databaseId = "YOUR_DATABASE_ID";

// Notion APIを使って新しいページを作成する関数
function createNotionPage() {
  const url = '<https://api.notion.com/v1/pages>';
  
  const headers = {
    'Authorization': 'Bearer ' + notionToken,
    'Content-Type': 'application/json',
    'Notion-Version': '2022-06-28',  // Notion APIのバージョン
  };

  const titleText = '新しいイベントのタイトル';  // タイトル
  const startDate = '2024-12-17T10:00:00Z';  // 開始日時 (ISO 8601形式: YYYY-MM-DDTHH:mm:ssZ)
  const endDate = '2024-12-17T12:00:00Z';  // 終了日時 (ISO 8601形式: YYYY-MM-DDTHH:mm:ssZ)

  const data = {
    parent: { database_id: notionDatabaseId },
    properties: {
      title: {  // Notionのタイトルプロパティ
        title: [
          {
            text: {
              content: titleText,  // ここに所定のタイトルを設定
            },
          },
        ],
      },
      "来店日": {  // Notionデータベースに設定された日付プロパティ名(例: '日付')
        date: {
          start: startDate,  // 開始日時
          end: endDate,      // 終了日時
        },
      },
    },
  };

  const options = {
    method: 'POST',
    headers: headers,
    payload: JSON.stringify(data),
  };

  try {
    const response = UrlFetchApp.fetch(url, options);
    Logger.log('Success: ' + response.getContentText());  // レスポンスの詳細をログに記録
  } catch (error) {
    Logger.log('Error: ' + error.message);  // エラーの詳細をログに記録
  }
}

このコードでNotionデータベース側に新規ページが追加されることを確認して、次の段階へ。

Google Calendarからのイベント取得機能の実装

次は、Google Calendarからのイベントを取得するためのコードを生成します。

const CALENDAR_ID = "[email protected]";  // 使用するカレンダーID
const LAST_RUN_KEY = "lastRunTime";  // 前回実行時刻の保存キー

function fetchNewEvents() {
  var properties = PropertiesService.getScriptProperties();
  var lastRunTime = properties.getProperty(LAST_RUN_KEY);

  // 現在時刻と半年後を設定
  var now = new Date();
  var sixMonthsLater = new Date();
  sixMonthsLater.setMonth(now.getMonth() + 6);

  // 前回の実行時刻があればそれを基準に設定
  var lastRunDate = lastRunTime ? new Date(lastRunTime) : new Date(0);

  // カレンダーを取得
  var calendar = CalendarApp.getCalendarById(CALENDAR_ID);

  // 前回実行時刻から半年後までのイベントを取得
  var events = calendar.getEvents(lastRunDate, sixMonthsLater);

  // 新しいイベントの処理
  if (events.length > 0) {
    for (var i = 0; i < events.length; i++) {
      var event = events[i];
      var createdTime = event.getLastUpdated();  // イベントの保存(更新)時刻を取得

      // **イベントの保存時刻が前回の実行時刻より新しい場合のみ処理**
      if (createdTime > lastRunDate) {
        var title = event.getTitle();
        var startTime = event.getStartTime().toISOString(); // ISO8601形式
        var endTime = event.getEndTime().toISOString();

        // イベント情報をログに出力
        Logger.log("新しいイベントが取得されました:");
        Logger.log("タイトル: " + title);
        Logger.log("開始時間 (ISO8601): " + startTime);
        Logger.log("終了時間 (ISO8601): " + endTime);
      }
    }
  } else {
    Logger.log("新しいイベントはありません。");
  }

  // 現在時刻を次回の基準時刻として保存
  properties.setProperty(LAST_RUN_KEY, now.toISOString());
}

  const options = {
    method: "POST",
    headers: headers,
    payload: JSON.stringify(data),
  };

  try {
    const response = UrlFetchApp.fetch(url, options);
    Logger.log("Success: " + response.getContentText());
  } catch (error) {
    Logger.log("Error: " + error.message);
  }
}

前回のコード実行時のタイムログを基準に、その時点から現在までの間で新規イベントが作成された場合、イベントタイトル・開始時間・終了時間を取得してログを表示する内容になっています。
なお、新規イベントの確認期間は現在の日付から半年です。
新規イベントが見当たらない場合は、その旨がログに表示されます。

両機能の統合

最後に、両機能を統合します。

const CALENDAR_ID = "[email protected]";  // 使用するカレンダーID
const LAST_RUN_KEY = "lastRunTime";  // 前回実行時刻の保存キー

// Notion API設定
const notionApiKey = "YOUR_API_KEY";
const databaseId = "YOUR_DATABASE_ID";

function fetchNewEvents() {
  var properties = PropertiesService.getScriptProperties();
  var lastRunTime = properties.getProperty(LAST_RUN_KEY);

  // 現在時刻と半年後を設定
  var now = new Date();
  var sixMonthsLater = new Date();
  sixMonthsLater.setMonth(now.getMonth() + 6);

  // 前回の実行時刻があればそれを基準に設定
  var lastRunDate = lastRunTime ? new Date(lastRunTime) : new Date(0);

  // カレンダーを取得
  var calendar = CalendarApp.getCalendarById(CALENDAR_ID);

  // 前回実行時刻から半年後までのイベントを取得
  var events = calendar.getEvents(lastRunDate, sixMonthsLater);

  // 新しいイベントの処理
  if (events.length > 0) {
    for (var i = 0; i < events.length; i++) {
      var event = events[i];
      var createdTime = event.getLastUpdated();  // イベントの保存(更新)時刻を取得

      // **イベントの保存時刻が前回の実行時刻より新しい場合のみ処理**
      if (createdTime > lastRunDate) {
        var title = event.getTitle();
        var startTime = event.getStartTime().toISOString(); // ISO8601形式
        var endTime = event.getEndTime().toISOString();

        // イベント情報をログに出力
        Logger.log("新しいイベントが取得されました:");
        Logger.log("タイトル: " + title);
        Logger.log("開始時間 (ISO8601): " + startTime);
        Logger.log("終了時間 (ISO8601): " + endTime);

        // Notionページを作成
        createNotionPage(title, startTime, endTime);
      }
    }
  } else {
    Logger.log("新しいイベントはありません。");
  }

  // 現在時刻を次回の基準時刻として保存
  properties.setProperty(LAST_RUN_KEY, now.toISOString());
}

// Notion APIを使って新しいページを作成する関数
function createNotionPage(title, startTime, endTime) {
  const url = "<https://api.notion.com/v1/pages>";
  
  const headers = {
    "Authorization": "Bearer " + NOTION_TOKEN,
    "Content-Type": "application/json",
    "Notion-Version": "2022-06-28",
  };

  const data = {
    parent: { database_id: NOTION_DATABASE_ID },
    properties: {
      "title": {  // タイトルプロパティ
        title: [
          {
            type: "text",  // type: "text" を追加して指定
            text: {
              content: title,  // ここにイベントタイトルを設定
            },
          },
        ],
      },
      "来店日": {  // Notion側の「日付」プロパティ名を指定
        date: {
          start: startTime,
          end: endTime,
        },
      },
    },
  };

  const options = {
    method: "POST",
    headers: headers,
    payload: JSON.stringify(data),
  };

  try {
    const response = UrlFetchApp.fetch(url, options);
    Logger.log("Success: " + response.getContentText());
  } catch (error) {
    Logger.log("Error: " + error.message);
  }
}

最後にタイムトリガーを作成して、任意の時間感覚ででGASを発火させるトリガーを作成し、完成です。


効果的だったChatGPTとの対話方法

今回の開発を通じて、プログラミング初心者でもChatGPTを適切に活用することで、実用的なツール開発が可能であることが実証できました。
特に、以下の点が成功の鍵となったかと思います。

複雑な機能を小さな単位に分解し、段階的に質問する
エラーが発生した際の具体的な状況説明と、原因究明のための効果的な質問を行う
基本機能から応用機能へと段階的に開発を進めていく

効果的な開発手順

特に複雑な機能を小さな単位に分解するパートにおいては、開発の計画段階でChatGPTに任せても良かったかもしれません。

これらの方法により、GASやAPIといった初めての技術要素に対しても、一歩ずつ着実に理解を深めながら開発を進めることができました。そういう意味では、ChatGPTは単なるコード生成ツールではなく、プログラミング学習における効果的な教師として機能すると言えます。


今後の展望

ここまでで、Google CalendarとNotionを連携させた基本的な予約管理システムの構築が完了しました。以下では、このツールの解決すべき課題と、更なる発展に向けた展望について詳しく説明していきます。

現状の課題

本ツールの主要な課題として、データ同期の一方向性が挙げられます。

現在のシステムではGoogle CalendarからNotionへの連携のみが実装されており、Notion側での予定の変更や追加がGoogle Calendarに反映されません。このような一方向の連携システムでは、両プラットフォーム間でデータの不整合が発生するリスクが存在します。

今後の展望

現在、Google CalendarとNotionデータベース間の完全な双方向同期については、実装が複雑になるという見通しを持っています。そのため、当面は一方向の同期に焦点を当て、その信頼性と安定性の向上を優先的に進めていく方針です。

現実的なアプローチとして、以下のようなチェック機能の導入を検討しています。

イベントIDによる関連付けの確認
開始時間と終了時間の一致確認
データの不整合が検出された場合、Slackへの自動通知機能

これにより、データの不整合を早期に発見し、必要な修正対応を迅速に行うことが可能となります。

まとめ

ChatGPTを活用した開発を通じて、以下のような学びが得られました:

  1. 段階的なアプローチの重要性

    • 機能を小さく分割して実装する

    • 各段階で確実に動作確認を行う

  2. 効果的な質問方法

    • 具体的な機能について質問する

    • エラー発生時は状況を詳しく説明する

  3. 実装の優先順位付け

    • まずは基本機能を確実に実装する

    • 段階的に機能を追加・改善していく

今回の開発経験から、ChatGPTを活用する際は、一度に完璧な実装を目指すのではなく、段階的に機能を実装していくアプローチが効果的だということがわかりました。
また、エラーが発生した際も、機能を分割して原因を特定していく方法が、効率的な問題解決につながることを学びました。

このように、プログラミング初心者でもChatGPTを活用することで、複雑なツール開発を段階的に進めることができ、実用的なシステムを構築することが可能です。
今後も、ユーザーのニーズに応じて機能を拡張しながら、より使いやすく信頼性の高いツールへと発展させていきたいと考えています。

いいなと思ったら応援しよう!