見出し画像

【AIで】クラスルームから時間割を作る!①ーGoogleサイトで作るグループウェア(124)ー

🙇🏻いつも、Googleサイトで作るグループウェアを見ていただき、ありがとうございます!


この記事を読んで欲しい方

企業DXや校務DXの進め方に悩んでいる方
クラウドアプリの導入に悩んでいる方
自分だけのGoogleサイトを作ってみたい方

①AIでクラスルームから時間割を作る

 以前、スプレッドシートで作った時間割クラスルームをハイパーリンクで連動させ、Googleサイトに埋め込んで時間割からクラスルームへ飛ぶ内容の投稿をしました。

 実際に、私のクラスで使っているのですが、とても使いやすいです。
 ただ、この仕組みを手動で作るとなると、かなり面倒くさいんですね。
 それで、GeminiAdvanceを使って、GASでクラスルームのデータから時間割を生成するプログラムが作れないかなと考えたのです。
 GASに関しては、ノーコード指向の私としては、あまり使いたくないのですが、GeminiAdvanceがGASを吐いてくれるなら、まあこれもノーコードだろうということで、やってみました。

②開発の流れ

 開発の流れは、この様になっています。スプレッドシートの中に、GASを作成し、そこから①クラスルームのクラスデータをAPI経由で取得し、スプレッドシートのクラス情報シートに保存します。
 次に、②クラス情報シートのデータから、時間割の表を生成します。

クラスルームから時間割の表を自動生成させる

③クラスルームの中身

  早速、GeminiでClassroomAPIで取ってこれるクラス情報を調べてみます。

  • id:コースの一意な ID

  • name:コースの名前

  • section:セクション名(存在する場合)

  • descriptionHeading:コースの説明のヘッダー(存在する場合)

  • description:コースの説明(存在する場合)

  • room:教室名(存在する場合)

  • ownerId:コース所有者の ID

  • creationTime:コースの作成時刻

  • updateTime:コースの最終更新時刻

  • その他の情報

続けて、その他の情報も見てみます。

コースの状態

  • courseState: コースがアーカイブされているか、アクティブか、準備中かを示します。

    • ACTIVE:アクティブなコース

    • ARCHIVED:アーカイブされたコース

    • PROVISIONED:準備中のコース

  • enrollmentCode: コースへの参加に使用する登録コード

教師と生徒の情報

  • teacherGroupEmail: コースの教師のグループメール

  • studentGroupEmail: コースの生徒のグループメール

保護者の情報

  • guardiansEnabled: コースで保護者の連絡が有効になっているかどうかを示します。

その他

  • alternateLink: コースの Classroom ウェブページへのリンク

  • calendarId: コースに関連付けられた Google カレンダーの ID

  • courseGroupEmail: コースのグループメールアドレス

  • courseMaterialSets: コース教材セットのリスト

  • teacherFolder: 教師用フォルダの情報

 この中で、必要そうな以下の項目を選びました。

"id", "name", "section", "descriptionHeading", "description", "room", "ownerId", "creationTime", "updateTime", "enrollmentCode", "courseState", "alternateLink", "teacherGroupEmail", "studentGroupEmail", "guardiansEnabled"

④クラスルームに時間割用のデータを入れる

 そもそも、クラスのデータには、曜日と時限のデータがありません

クラスルーム
クラスの詳細には曜日と時限がない

 そこで、曜日のデータはセクション:sectionに、時限のデータは部屋:roomに入れることにしました。
 クラス名(必須)には科目名:nameが入っています。
 また、科目名のハイパーリンクを作るためのURLalternateLinkから、アーカイブされていないクラスデータを取るためには、courseStateを使います。

⑤GeminiからGASを吐き出させる

 まず、GeminiからクラスルームAPIを使って、クラスデータをGASで取り出して、スプレッドシートのクラス情報シートに保存するように指示します。
 ここでは、なんどか試行錯誤してプログラムを吐き出させました。

function getCreatedCourseDataAndWriteToSheet() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getSheetByName('クラス情報'); // シート名を指定

  var courses = Classroom.Courses.list().courses;
  if (courses && courses.length > 0) {
    var data = [];
    var header = ["id", "name", "section", "descriptionHeading", "description", "room", "ownerId", "creationTime", "updateTime", "enrollmentCode", "courseState", "alternateLink", "teacherGroupEmail", "studentGroupEmail", "guardiansEnabled"];
    data.push(header); // ヘッダー行を追加

    var filterDate = new Date("2020-04-01T00:00:00+09:00"); // デフォルト期間で2020年4月1日 JST 以降のデータを取る

    for (var i = 0; i < courses.length; i++) {
      var course = courses[i];
      var creationTime = new Date(course.creationTime); // creationTime を Date オブジェクトに変換
      if (creationTime >= filterDate && course.courseState === "ACTIVE") { // 2020年4月1日以降のcreationTimeのみと、アーカイブを除外する
        data.push([
          course.id,
          course.name,
          course.section,
          course.descriptionHeading,
          course.description,
          course.room,
          course.ownerId,
          course.creationTime,
          course.updateTime,
          course.enrollmentCode,
          course.courseState,
          course.alternateLink,
          course.teacherGroupEmail,
          course.studentGroupEmail,
          course.guardiansEnabled
        ]);
      }
    }
    sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
  } else {
    Logger.log('No courses found.');
  }
}

 次にスプレッドシートのクラス情報シートを読み取って時間割表を作ってもらいました。
 最初はGeminiがあまり理解してくれず、苦戦しましたが、Geminiの理解のクセを読みながら、なんとか完成させました。ただ、出来上がったのはPythonのプログラムです。
 ここで、コードをGASに変換してくれなにげに頼むと、なんということでしょう!PythonからGASに変換してくれました。
 それを、AIに少し手直しをしてもらって、出来上がったのが以下のコードです。
 ここまで、Geminiと対話しながら作ってもらい、自分は一切プログラムを書いていません。

function createTimetable() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName("クラス情報");
  const lastRow = sheet.getLastRow();
  const data = sheet.getRange(2, 1, lastRow - 1, sheet.getLastColumn()).getValues();

  const weekdayDict = {'月': 1, '火': 2, '水': 3, '木': 4, '金': 5, '土': 6, '日': 7};

  const timetable = {};
  for (const row of data) {
    const weekday = row[2];
    const period = row[5]; 
    const name = row[1];
    const link = row[11];

    if (!timetable[period]) {
      timetable[period] = {};
    }
    timetable[period][weekday] = `=HYPERLINK("${link}", "${name}")`;
  }

  const outputSheet = ss.getSheetByName("時間割") || ss.insertSheet("時間割");
  outputSheet.clearContents();

  const header = [""].concat(Object.keys(weekdayDict).sort((a, b) => weekdayDict[a] - weekdayDict[b]));
  outputSheet.appendRow(header);

  // periods変数は不要になったため削除
  for (const period in timetable) { 
    const row = [period];
    for (const weekday of header.slice(1)) {
      row.push(timetable[period][weekday] || "");
    }
    outputSheet.appendRow(row);
  }
}

⑥GASの完成

 あとは、私がよく使っているGASのメニュー表示を追加して、プログラムが完成しました。
 実際に、私が直に手を入れた部分は5%程度で、95%はGeminiが生成したプログラムです。

function onOpen() {
  var ui = SpreadsheetApp.getUi();
  var menu = ui.createMenu('【自動化】');
  menu.addItem('1.許可', 'permission');
  menu.addItem('2.クラスデータ取得', 'getCreatedCourseDataAndWriteToSheet');
  menu.addItem('3.時間割作成', 'createTimetable');
  menu.addToUi();
  /**Browser.msgBox('【自動化】メニューが起動しました。\\n「1.許可」を実行してプログラムの組み込みを許可してください。'); */
}

function permission() {
  Browser.msgBox('プログラムの組み込みを許可しました。');
}

function getCreatedCourseDataAndWriteToSheet() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getSheetByName('クラス情報'); // シート名を指定

  var courses = Classroom.Courses.list().courses;
  if (courses && courses.length > 0) {
    var data = [];
    var header = ["id", "name", "section", "descriptionHeading", "description", "room", "ownerId", "creationTime", "updateTime", "enrollmentCode", "courseState", "alternateLink", "teacherGroupEmail", "studentGroupEmail", "guardiansEnabled"];
    data.push(header); // ヘッダー行を追加

    var filterDate = new Date("2020-04-01T00:00:00+09:00"); // デフォルト期間で2020年4月1日 JST 以降のデータを取る

    for (var i = 0; i < courses.length; i++) {
      var course = courses[i];
      var creationTime = new Date(course.creationTime); // creationTime を Date オブジェクトに変換
      if (creationTime >= filterDate && course.courseState === "ACTIVE") { // 2020年4月1日以降のcreationTimeのみと、アーカイブを除外する
        data.push([
          course.id,
          course.name,
          course.section,
          course.descriptionHeading,
          course.description,
          course.room,
          course.ownerId,
          course.creationTime,
          course.updateTime,
          course.enrollmentCode,
          course.courseState,
          course.alternateLink,
          course.teacherGroupEmail,
          course.studentGroupEmail,
          course.guardiansEnabled
        ]);
      }
    }
    sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
  } else {
    Logger.log('No courses found.');
  }
}

function createTimetable() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName("クラス情報");
  const lastRow = sheet.getLastRow();
  const data = sheet.getRange(2, 1, lastRow - 1, sheet.getLastColumn()).getValues();

  const weekdayDict = {'月': 1, '火': 2, '水': 3, '木': 4, '金': 5, '土': 6, '日': 7};

  const timetable = {};
  for (const row of data) {
    const weekday = row[2];
    const period = row[5]; 
    const name = row[1];
    const link = row[11];

    if (!timetable[period]) {
      timetable[period] = {};
    }
    timetable[period][weekday] = `=HYPERLINK("${link}", "${name}")`;
  }

  const outputSheet = ss.getSheetByName("時間割") || ss.insertSheet("時間割");
  outputSheet.clearContents();

  const header = [""].concat(Object.keys(weekdayDict).sort((a, b) => weekdayDict[a] - weekdayDict[b]));
  outputSheet.appendRow(header);

  // periods変数は不要になったため削除
  for (const period in timetable) { 
    const row = [period];
    for (const weekday of header.slice(1)) {
      row.push(timetable[period][weekday] || "");
    }
    outputSheet.appendRow(row);
  }
}

 ※注意:GASのサービス+でClassroomAPIをチェックしておくこと。

 長くなっているので、次の回で実際に使ってみましょう。

この記事が参加している募集

この記事が気に入ったらサポートをしてみませんか?