動的 Google フォーム!- Google カレンダー を使わない!!三者面談予約 Ver.3 & Ver.4
今回は、動的な Google フォーム を連携させて使うシステムです!!
※最後に、なんと、課題があります🤣笑
かなり運用度が上がっていると思いますので、参考にしてください!
今回もめっちゃ長いので、最初に動画を上げておきます!
前回までの記事を合わせてご覧ください!
(ちょっと前置きが多いです、すみません!🙇♂️)
また、今回は、こちらで紹介しているものを使っています!
上記で、参考にさせていただいているサイトはこちらです。
本題と関係ないんですが、ChatGPT を使ってプログラミングすることが多いんですけど、色々試してたら、こんな結果が出てきたんですよ…。
上が、これまで上記の管理人様に連絡をして使っていたもの。
下が、ChatGPT から返ってきたもの。
コメントまで同じ🤣笑
2023年2月26日現在で、2021年までのデータでやっているっぽいから、このウェブサイト(それか Git ?)も学習されているんですね。
こうやってコードも学習されて使われるんですね。
そりゃいろんなところで声を上げる人多い訳ですね🤔
公開する情報は、考えないといけないのかもしれませんね。
note ならここから下は購入者限定!とか。
話が逸れました。
それでは本題。
[1] Google フォーム の作成
回答は、「メールアドレスを収集」をオンにしてください!
そして、最初に「タイトルと説明を追加」から
こんな感じで予約枠の一覧を作りましょう!
この時、日付と時間の間は、半角スペースを入れてください!!
よく確認をしていただくと、3/22(水)と11:00〜11:50の間にスペースを入れてあります。
この後のプログラムで、このスペースで分割してカレンダーを認識するように作っています!!
希望日時という質問を追加して、プルダウンにして、選択肢を入れましょう!
(今回は、プルダウンとして動くように作ってあります)
この時、先ほどの予約枠一覧をコピペした方が早いし確実です!
何が確実かというと、、、、
これから、この選択肢の解答からスプレッドシートに入力していくんですが、それぞれの文字列(今回で言うと3/22(水) 11:00〜11:50とか)は、スペースも含めて、完全に一致していないといけないのです🤓
あとは、前回までの記事と一緒です!
別にセクションは分けなくても良いのですが、なんとなく分けてます笑
あとは、回答のスプレッドシートを作成しておきましょう!
[2] スプレッドシートの準備
スプレッドシートには、このようにシートを追加してください。
教師カレンダー
日程
予約
教師カレンダーのシートは、↓のように、フォームでしてある日時を表にしておきます!
日程のシートは、↓のように、日程、定員、予約済の列を作り、定員と現在の予約数を入力する欄を作ります。
この時の日程は、フォームの選択肢と一致していないといけないので、フォームのテキストからコピペした方が良いです!
予約のシートは、↓のように一覧を転記するシートになっています。
こんな感じで準備してください!
ここまででも大分疲れますね😅笑
もう少しがんばりましょう!!!!💪
[3] スクリプトの準備
今回大事なのは、この Google フォーム にスクリプトを入れる!
と言うことです。
今回は、どこか希望日時が入力されたら、その日時は選択できないようにします。
よって、Google フォーム を編集し直す必要があるんです!
右上にある3点リーダー(・・・)から、「スクリプト エディタ」をクリックしましょう!
追加するスクリプトはこちらです。
const form = FormApp.getActiveForm();
const spreadSheet = SpreadsheetApp.openById('');//ここは、スプレッドシートのIDを入れる
const reserveSheet = spreadSheet.getSheetByName('予約');
const listSheet = spreadSheet.getSheetByName('日程');
const calender = spreadSheet.getSheetByName('教師カレンダー');
// *--------------------*
// フォーム送信時の処理
// *--------------------*
function receivedApplication(e) {
// フォームの送信内容
const email = e.response.getRespondentEmail();
const items = e.response.getItemResponses();
const preferredDate = (items[0].getItem().getTitle() === "希望日時" ? items[0].getResponse() : "");
let result = "";
if (preferredDate) {
// 日程シートチェック&更新
result = checkAvailability(preferredDate);
// 予約シートに申込内容を書き込み
if (result = "OK") { reserveSheet.appendRow([email, preferredDate]); }
// フォームを更新
editForm();
} else {
result = "NG";
}
// メール送信
sendEmail(email, preferredDate, result);
}
// *---------------------*
// 予約状況をチェックする
// *---------------------*
function checkAvailability(preferredDate) {
// listシートを配列に格納
const list = listSheet.getDataRange().getValues();
list.shift();
// リストから予約日を探し、定員を確認する
for (let i = 0; i < list.length; i++) {
if (list[i][0] == preferredDate) {
// 予約済 < 定員であればOK
if (list[i][2] < list[i][1]) {
listSheet.getRange(i + 2, 3).setValue(list[i][2] + 1);
// 回答から日付と時間帯を取得する例
var data = calender.getDataRange().getValues();
var dateAndTime = preferredDate.split(" ");
var date = dateAndTime[0];
var time = dateAndTime[1];
Logger.log(date);
Logger.log(time);
//該当の日時を検索して、そのカラム番号、列番号を取得
for (var k = 0; k < data.length; k++) {
for (var j = 0; j < data[k].length; j++) {
if (data[k][j] == date) {
dataColumn = j + 1;
}
else if (data[k][j] == time) {
dataRow = k + 1;
}
}
}
calender.getRange(dataRow, dataColumn).setValue('予約あり');
return "OK";
} else {
return "NG";
}
}
}
// 希望日がリストに存在しなかった場合はNG
return "NG";
}
// *----------------------*
// 予約結果をメール送信する
// *----------------------*
function sendEmail(email, preferredDate, result) {
const mailTitle = "予約結果について";
let mailBody;
if (result == "OK") {
mailBody = "予約が完了しました。\n"
+ `予約日:${preferredDate}`
+ '\n\nよろしくお願いします。'
} else {
mailBody = "定員超過のため予約できませんでした。\n"
+ "下記のフォームから再度申請してください\n"
+ form.getPublishedUrl();
}
// 結果メール送信
GmailApp.sendEmail(email, mailTitle, mailBody);
}
// *-----------------*
// フォームを更新する
// *-----------------*
function editForm() {
let infoText = ""; // 「空き状況」部分のテキスト
let choiceValues = []; // 「参加希望日」部分の選択肢
// 日程シートを配列に格納
const list = listSheet.getDataRange().getValues();
list.shift();
// 日程シートのデータからフォームの内容を作成する
for (const record of list) {
if (record[2] < record[1]) {
// 空きがある日程は「空き状況」に記載+選択肢として設定
infoText += `${record[0]} : 残り ${record[1] - record[2]} 名\n`;
choiceValues.push(record[0]);
} else {
// 空きがない日程は「空き状況」に満員を記載+選択肢にはしない
infoText += `${record[0]} : 満員(申込不可)\n`;
}
}
// 選択肢が1つもない場合、選択肢は「全日程申込不可」としフォームをクローズ
if (!choiceValues.length) {
choiceValues.push("全日程申込不可");
form.setAcceptingResponses(false);
}
// フォームに変更を反映
const items = form.getItems();
items[0].setHelpText(infoText);
items[1].asListItem().setChoiceValues(choiceValues);
}
これでOK!
あとは回答を収集していくスプレッドシートのIDを↓の ' ' の間に入れましょう!
const spreadSheet = SpreadsheetApp.openById('');//ここは、スプレッドシートのIDを入れる
もう、いろんな記事を見ていただいている方は、IDの場所は大丈夫ですよね!?🤣笑
どうしてもと言う方は、末尾の動画をご覧ください!笑
(ちょっと楽しました、すみません笑)
それでは、💾(保存)をして、▶️(実行)をクリックしょましょう!
いつものように権限の承認ステップがあります!
そしたら次にトリガーの設定です!
トリガーは左側の⏰(トリガー)から!
このように、 recievedApplication の関数のまま、「イベントの種類」を「フォーム送信時」にして保存してください!
場合によって、ここでまた権限を許可してください的な警告が出てくるので、同じように許可をしてください!
[4] テスト
それではテストをしてみましょう!
フォームの「プレビュー」から、
希望日時を選択します。
あとはテキトーに。
送信すると….。
満員となり….。
選択肢からも外れていますね!😆
それぞれのシートはこんな感じになります!
良い感じですね!!😆
ぜひご活用ください!
ただですね、これ、何回かテストすると、元の状態に戻すのがめちゃくちゃ面倒なんです….。
そんな時は、、、。
リセット用スクリプト
回答を集約しているスプレッドシートにある機能を入れると楽に….。
こちらをコピペし、
function onOpen() {
SpreadsheetApp.getActiveSpreadsheet().addMenu('リセットメニュー', [
{ name: 'フォームの選択肢をリセット', functionName: 'updateFormList' },
{ name: 'フォーム内の回答全削除', functionName: 'alldelete' },
{ name: '日程・予約シートのリセット', functionName: 'cellrst' },
]);
}
function updateFormList() {
//フォームの質問番号
var questionNo = 2;
//フォームID
var formId = '';//フォームのIDを入れます
//シートID
var sheetId = '';//このスプレッドシートのIDを入れます。
//シート名
var sheetName = '日程';
//データ配置列名
var colName = 'A';
//データ開始行
var rowNum = 2;
//フォームの取得
var form = FormApp.openById(formId);
//全質問項目を取得
var items = form.getItems();
//questionNoで指定した質問項目を指定(配列は0から始まるので-1)
var item = items[questionNo - 1];
//スプレッドシートを取得
var sheet = SpreadsheetApp.openById(sheetId).getSheetByName(sheetName);
//シートの最終行を取得
var lastRow = sheet.getLastRow();
//リスト内容の取得
var arrList = sheet.getRange(colName + rowNum + ':' + colName + lastRow).getValues();
let infoText = ""; // 「空き状況」部分のテキスト
// 日程シートを配列に格納
const list = sheet.getDataRange().getValues();
list.shift();
// 日程シートのデータからフォームの内容を作成する
for (const record of list) {
// 空きがある日程は「空き状況」に記載+選択肢として設定
infoText += `${record[0]} : 残り 1 名\n`;
}
//選択肢をフォームに反映
items[0].setHelpText(infoText);
items[1].asListItem().setChoiceValues(arrList);
}
function alldelete() {
var Form = FormApp.openByUrl('');//フォームの編集画面の時のURLを入れます。
Form.deleteAllResponses();
}
function cellrst() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sht = ss.getSheetByName('日程');
var sht2 = ss.getSheetByName('予約');
var sht3 = ss.getSheetByName('教師カレンダー');
var lastrow = sht.getLastRow();
var lastrow2 = sht2.getLastRow();
var lastrow3 = sht3.getLastRow();
var lastcolumn3 = sht3.getLastColumn();
for (let i = 2; i <= lastrow; i++) {
sht.getRange(i, 3).setValue('0');
}
sht2.getRange(2, 1, lastrow2, 2).clearContent();//1行目、5列目から、、、1行目3列目までを削除
//スクリプトに紐づいたスプレッドシートのアクティブなシートを読み込み
sht3.getRange(2, 2, lastrow3, lastcolumn3).clearContent();
}
下記にそれぞれのIDを入れる
//フォームID
var formId = '';//フォームのIDを入れます
//シートID
var sheetId = '';//このスプレッドシートのIDを入れます。
ここに、Google フォーム を編集(作っている時の)している時の画面の URL を入れてください。
var Form = FormApp.openByUrl('');//フォームの編集画面の時のURLを入れます。
これでOK!
先ほどと同じように、💾(保存)をして、▶️(実行)をクリックしょましょう!
もちろん、権限は許可してくださいね!
そうすると…。
こんな感じでリセットメニューが出てくるので、これを使えば楽に元の状態に戻せます👍✨
あれ、これもう相当有料級だと思うんですけど笑
Ver.1 の動画
Ver.2 の動画
Ver.3 の動画
Ver.4 について
Ver.4 は、この Ver.3 と Ver.2 を組み合わせた感じになっています!!
そのため、動画や、これまでボクがあげた動画を参考にして、ぜひ挑戦してください!😎
課題は
です。
動画でも解説しているのと、
解答はこちらです!
const form = FormApp.getActiveForm();
const spreadSheet = SpreadsheetApp.openById("");
const reserveSheet = spreadSheet.getSheetByName("予約");
const listSheet = spreadSheet.getSheetByName("日程");
const calender = spreadSheet.getSheetByName("教師カレンダー");
//保護者カレンダーを作りたい時↓
const sheet = SpreadsheetApp.openById('').getSheetByName('保護者カレンダー');
// *--------------------*
// フォーム送信時の処理
// *--------------------*
function receivedApplication(e) {
// フォームの送信内容
const email = e.response.getRespondentEmail();
const items = e.response.getItemResponses();
const preferredDate = (items[0].getItem().getTitle() === "希望日時" ? items[0].getResponse() : "");
let result = "";
const name = items[3].getResponse();
if (preferredDate) {
// 日程シートチェック&更新
result = checkAvailability(preferredDate, name);
// 予約シートに申込内容を書き込み
if (result = "OK") { reserveSheet.appendRow([email, preferredDate]); }
// フォームを更新
editForm();
} else {
result = "NG";
}
// メール送信
sendEmail(email, preferredDate, result);
}
// *---------------------*
// 予約状況をチェックする
// *---------------------*
function checkAvailability(preferredDate, name) {
// listシートを配列に格納
const list = listSheet.getDataRange().getValues();
list.shift();
// リストから予約日を探し、定員を確認する
for (let i = 0; i < list.length; i++) {
if (list[i][0] == preferredDate) {
// 予約済 < 定員であればOK
if (list[i][2] < list[i][1]) {
listSheet.getRange(i + 2, 3).setValue(list[i][2] + 1);
// 回答から日付と時間帯を取得する例
var data = calender.getDataRange().getValues();
var dateAndTime = preferredDate.split(" ");
var date = dateAndTime[0];
var time = dateAndTime[1];
Logger.log(date);
Logger.log(time);
//該当の日時を検索して、そのカラム番号、列番号を取得
for (var k = 0; k < data.length; k++) {
for (var j = 0; j < data[k].length; j++) {
if (data[k][j] == date) {
dataColumn = j + 1;
}
else if (data[k][j] == time) {
dataRow = k + 1;
}
}
}
calender.getRange(dataRow, dataColumn).setValue(name);
sheet.getRange(dataRow, dataColumn).setValue('予約あり');
return "OK";
} else {
return "NG";
}
}
}
// 希望日がリストに存在しなかった場合はNG
return "NG";
}
// *----------------------*
// 予約結果をメール送信する
// *----------------------*
function sendEmail(email, preferredDate, result) {
const mailTitle = "予約結果について";
let mailBody;
if (result == "OK") {
mailBody = "予約が完了しました。\n"
+ `予約日:${preferredDate}`
+ '\n\nよろしくお願いします。'
} else {
mailBody = "定員超過のため予約できませんでした。\n"
+ "下記のフォームから再度申請してください\n"
+ form.getPublishedUrl();
}
// 結果メール送信
GmailApp.sendEmail(email, mailTitle, mailBody);
}
// *-----------------*
// フォームを更新する
// *-----------------*
function editForm() {
let infoText = ""; // 「空き状況」部分のテキスト
let choiceValues = []; // 「参加希望日」部分の選択肢
// 日程シートを配列に格納
const list = listSheet.getDataRange().getValues();
list.shift();
// 日程シートのデータからフォームの内容を作成する
for (const record of list) {
if (record[2] < record[1]) {
// 空きがある日程は「空き状況」に記載+選択肢として設定
infoText += `${record[0]} : 残り ${record[1] - record[2]} 名\n`;
choiceValues.push(record[0]);
} else {
// 空きがない日程は「空き状況」に満員を記載+選択肢にはしない
infoText += `${record[0]} : 満員(申込不可)\n`;
}
}
// 選択肢が1つもない場合、選択肢は「全日程申込不可」としフォームをクローズ
if (!choiceValues.length) {
choiceValues.push("全日程申込不可");
form.setAcceptingResponses(false);
}
// フォームに変更を反映
const items = form.getItems();
items[0].setHelpText(infoText);
items[1].asListItem().setChoiceValues(choiceValues);
}
ぜひ挑戦してみてください!
ポートフォリオ
YouTube チャンネル
いちばんやさしい Google Apps Script
何かと0から1を作るのは大変だと思います。学校はどこも似たような問題課題に対応していると思います。それなのに、先生って自分だけで頑張ろうとするんですよね。ボクの資料やnoteが1になって、学校ごとの現状に合わせてカスタムしていただければと思います‼️