GASを活用して学年会レジュメに今後1ヶ月の予定を10秒で入力する。<googleドキュメント✖️GAS✖️googleカレンダー>
前回書いた「学年会のレジュメをgoogleドキュメントで一年間運用したらメリットだらけだった」で紹介したレジュメは学年メンバーと共有するためのファイルのリンクを貼ることでかなり生産性の上がる方法だったのですが、このドキュメントにすこーしだけGASを活用することでさらに生産性が上がったので紹介します。興味があればコピペして使ってみてください。
コードの叩き台は半年前に私自身が書いたものですが、 このnoteで公開するにあたって、あまりにも読みにくいコードのためchatGPTさんに整えてもらいました。
この話の前提として、本校では行事の予定を職員専用のgoogleカレンダーで共有しています。共有していなくても個人でカレンダーを使っていればこの運用ができます。
学校のカレンダー導入の経緯や導入の提案などの件はまた後で紹介します。
実際の使用感はこんな感じ。
1 {予定}と入力する。
2 メニューから「予定の追加」を選択する
3 10秒まつ
と言う具合で、1ヵ月に1回、この作業をするだけでオッケーです。結構簡単じゃないすか。
こんな作業をさせるコードを作った(作ってもらう)
googleカレンダーから一ヶ月分の予定を取得する。
googleドキュメントの中にある {今後の予定} という文字列を見つけて転記する。
onOpenでメニューを作る。
転記する際の条件
日付を見出し1にする
イベント名は箇条書きで標準テキストにする
終日イベントでないものは時間を入れる
イベントがない日も日付を入れる
区切り線で区切る
日付には曜日も入れる
chatGPTと協力してできたコード
興味ある方は、以下のコードをまるっとコピーして使用してみてくだい。
function onOpen() {
var ui = DocumentApp.getUi();
var menu = ui.createMenu('予定の追加');
menu.addItem('一ヶ月分の予定を{予定}に追記する', 'myFunction');
menu.addToUi();
}
function myFunction() {
var body = DocumentApp.getActiveDocument().getBody();
var startDate = new Date();
var period = 30; // 予定を表示したい日数
var calendarIDs = ['__________@group.calendar.google.com',
'____________@group.calendar.google.com'];
// カレンダーIDの配列。必要に応じて追加してください。
var eventsByDate = getEventsByDate(startDate, period, calendarIDs);
// 予定をドキュメントに追記
appendEventsToDocument(body, eventsByDate);
// 「00:00〜00:00」を削除
removeDoubleZeroTime(body);
}
function removeDoubleZeroTime(body) {
var paragraphs = body.getParagraphs();
for (var i = 0; i < paragraphs.length; i++) {
var text = paragraphs[i].getText();
if (text.indexOf('00:00〜00:00') !== -1) {
paragraphs[i].replaceText('00:00〜00:00', '');
}
}
}
// Googleカレンダーから予定取得
function getEventsByDate(startDate, period, calendarIDs) {
var eventsByDate = {};
for (var iCalendar = 0; iCalendar < calendarIDs.length; iCalendar++) {
var calendar = CalendarApp.getCalendarById(calendarIDs[iCalendar]);
var date = new Date(startDate);
for (var iDate = 0; iDate < period; iDate++) {
var dateString = _Md(date); // _Md()関数で日付と曜日を表示する形式に変更
var events = getDayEvents(calendar, date);
if (events.length > 0) {
eventsByDate[dateString] = events;
} else {
if (!eventsByDate[dateString]) {
eventsByDate[dateString] = [];
}
}
date.setDate(date.getDate() + 1);
}
}
return eventsByDate;
}
function getDayEvents(calendar, date) {
var events = calendar.getEventsForDay(date);
var dayEvents = [];
for (var iEvent = 0; iEvent < events.length; iEvent++) {
var event = events[iEvent];
var title = event.getTitle();
var startTime = event.getStartTime();
var endTime = event.getEndTime();
dayEvents.push({ title: title, startTime: startTime, endTime: endTime });
}
return dayEvents;
}
function appendEventsToDocument(body, eventsByDate) {
var keys = Object.keys(eventsByDate);
for (var i = 0; i < keys.length; i++) {
var dateStr = keys[i];
var events = eventsByDate[dateStr];
body.appendParagraph('<' + dateStr + '>').setHeading(DocumentApp.ParagraphHeading.HEADING1);
if (events.length > 0) {
for (var j = 0; j < events.length; j++) {
body.appendParagraph(formatEvent(events[j])); // イベントを整形して表示
}
} else {
// イベントがない場合でも日付のみ記載
body.appendParagraph(''); // 改行用の空行を追加
}
// 区切り線を追加
if (i < keys.length - 1) {
body.appendHorizontalRule();
}
}
}
function formatEvent(event) {
var startTime = event.startTime;
var endTime = event.endTime;
// 終日のイベントの場合
if (isAllDayEvent(startTime, endTime)) {
return '・' + event.title;
}
// 開始時間と終了時間が同じ場合(瞬間的なイベント)
if (startTime.getTime() === endTime.getTime()) {
var formattedTime = _HHmm(startTime);
return '・' + event.title + ' ' + formattedTime;
}
var formattedStartTime = _HHmm(startTime);
var formattedEndTime = _HHmm(endTime);
return '・' + event.title + ' ' + formattedStartTime + '〜' + formattedEndTime;
}
function isAllDayEvent(startTime, endTime) {
// 時間が00:00かつ終了時間が23:59なら終日のイベントと判定
return startTime.getHours() === 0 && startTime.getMinutes() === 0 &&
endTime.getHours() === 23 && endTime.getMinutes() === 59;
}
function _Md(date) {
var month = date.getMonth() + 1; // 月は0から始まるため+1する
var day = date.getDate();
var dayOfWeek = _JPdayOfWeek(date); // 曜日を取得する関数を呼び出す
return month + '月' + day + '日(' + dayOfWeek + ')';
}
function _JPdayOfWeek(date) {
var dayStr = ['日', '月', '火', '水', '木', '金', '土'];
var dayOfWeek = date.getDay();
return dayStr[dayOfWeek];
}
function _HHmm(date) {
return Utilities.formatDate(date, 'JST', 'HH:mm');
}
コード貼り付け後の注意
var calendarIDs = ['__________@group.calendar.google.com',
'____________@group.calendar.google.com'];
の部分は適宜自分が使用するカレンダーIDに変更してください。
ちなみに私は職員で共有しているカレンダーと学年カレンダーの二つの予定を取得したいので二つあります。ひとつで十分な人は削除して使用ください。
var period = 30; // 予定を表示したい日数
の部分は「30」が表紙させたい日数ですので、一週間に一度更新であれば「7」に変更します。
(おまけ)せっかくなのでコードの解説をしてもらったchatGPTさん
このコードは、Googleドキュメントに指定された期間のGoogleカレンダーのイベント情報を取得し、日付を見出し1の書式でドキュメントに追記するものです。
`onOpen()`: Googleドキュメントが開かれた際にメニューを追加する関数です。メニューを使って`myFunction()`を実行することができます。
`myFunction()`: メインの関数で、Googleカレンダーからイベントを取得し、それをドキュメントに追記します。また、終日イベントの場合は時間表示を省略するための処理も行っています。
`removeDoubleZeroTime(body)`: ドキュメント内のすべての「00:00〜00:00」を削除する関数です。
`getEventsByDate(startDate, period, calendarIDs)`: 指定された期間とカレンダーIDを元に、Googleカレンダーからイベントを取得する関数です。日付をキーとしてイベントを整理して返します。
`getDayEvents(calendar, date)`: 指定された日付に対するイベントを取得する関数です。イベント情報をオブジェクトの配列として返します。
`appendEventsToDocument(body, eventsByDate)`: 取得したイベント情報をドキュメントに追記する関数です。日付を見出し1の書式で追記し、イベント情報を箇条書きで追加します。
`formatEvent(event)`: イベント情報を整形する関数で、終日イベントや時間表示を処理して整形したテキストを返します。
`isAllDayEvent(startTime, endTime)`: イベントが終日イベントかどうかを判定する関数です。開始時間と終了時間が00:00から23:59までの場合は終日イベントと判定します。
`_Md(date)`: 日付を日付と曜日を表示する形式に変換する関数です。
`_JPdayOfWeek(date)`: 日付から曜日を取得する関数です。
`_HHmm(date)`: 時間をHH:mm形式の文字列に変換する関数です。
このように各関数が連携して、Googleカレンダーからイベントを取得してドキュメントに整形して追記する動作を行っています。
半年ほど運用してみた感想
どんな予定があったかなと 学年会の前にレジュメを作るときに、わざわざ資料を見て転記する必要がなくなり、 生産性は間違いなく上がりました。紙見ながら転記していいた自分に早く教えてあげたかった。
また先の予定も眺めておくことができ、学年メンバーの先生方も気づいたことだけでなく、 落ちていることや、抜けていることを補完して入力してくれるようになりました。 学年メンバーの組織力アップです。
この仕組みを興味持った2年目の先生はどういう仕組みなのか教えてほしいと声をかけてくれました。こうやってGASがちょっとずつ現場にも広がっていくと、 忙しい学校現場でも生産性が上がってるのかなと思います。
プログラミングと聞くと、なんだか難しそうだけど、初学者が基本の基本さえ覚えさえすれば、後はChatGPTなどのAIにやりたいことを投げて、 出てきた行動を実行して、修正プロンプトをかける。 その試行錯誤で、あっとい
う間に業務に使えるちょっとしたものができるんですよね。