回答編集履歴
6
test
CHANGED
@@ -58,7 +58,7 @@
|
|
58
58
|
const eventMark = '★';
|
59
59
|
|
60
60
|
// 転記元カレンダーID
|
61
|
-
const fromCalendarID = 'xxxxxxx@group.calendar.google.com';
|
61
|
+
const fromCalendarID = 'xxxxxxx@group.calendar.google.com';
|
62
62
|
|
63
63
|
// メンバーの名前とカレンダーIDを記入したシート名
|
64
64
|
const idSheetName = 'IDマスタ';
|
@@ -67,72 +67,131 @@
|
|
67
67
|
const startRow = 1;
|
68
68
|
|
69
69
|
// スプレッドシートを取得
|
70
|
-
const ss = SpreadsheetApp.getActiveSpreadsheet()
|
70
|
+
const ss = SpreadsheetApp.getActiveSpreadsheet();
|
71
71
|
|
72
|
-
// メンバーごとの氏名とカレンダーIDをシートから取得
|
72
|
+
// メンバーごとの氏名とカレンダーIDをシートから取得
|
73
73
|
const idSheet = ss.getSheetByName(idSheetName);
|
74
|
+
if (!idSheet) {
|
74
|
-
cons
|
75
|
+
console.log(`${idSheetName}シートが見つかりませんでした`);
|
75
|
-
|
76
|
+
return;
|
77
|
+
}
|
76
78
|
|
79
|
+
const idValues = idSheet.getDataRange().getDisplayValues();
|
80
|
+
const members = idValues
|
81
|
+
.filter(v => v[0] && v[1]) // 空の行をフィルタリング
|
82
|
+
.map(v => ({ name: v[0].trim(), id: v[1].trim() }));
|
83
|
+
|
84
|
+
if (members.length === 0) {
|
85
|
+
console.log(`${idSheetName}シートに有効なメンバーデータがありません`);
|
86
|
+
return;
|
87
|
+
}
|
88
|
+
|
77
|
-
// 登録元カレンダーから翌月末日までの予定
|
89
|
+
// 登録元カレンダーから翌月末日までの予定を取得
|
78
90
|
const calendar = CalendarApp.getCalendarById(fromCalendarID);
|
79
91
|
const today = new Date();
|
80
|
-
const next
|
92
|
+
const nextMonthEnd = new Date(today.getFullYear(), today.getMonth() + 2, 0);
|
93
|
+
|
81
|
-
const schedules = calendar.getEvents(today, next
|
94
|
+
const schedules = calendar.getEvents(today, nextMonthEnd)
|
82
95
|
.map(e => ({
|
83
96
|
title: e.getTitle(),
|
84
97
|
start: e.getStartTime(),
|
85
98
|
end: e.getEndTime(),
|
86
|
-
description: e.getDescription(),
|
99
|
+
description: e.getDescription() ?? '',
|
87
100
|
}));
|
88
101
|
|
102
|
+
console.log(`取得した予定数: ${schedules.length}`);
|
103
|
+
|
104
|
+
// メンバーごとのカレンダーオブジェクトを事前に取得・キャッシュ
|
105
|
+
const memberCalendars = new Map();
|
106
|
+
for (const member of members) {
|
107
|
+
const cal = CalendarApp.getCalendarById(member.id);
|
108
|
+
memberCalendars.set(member.id, cal);
|
109
|
+
}
|
110
|
+
|
89
|
-
//
|
111
|
+
// バッチ処理用の配列
|
112
|
+
const sheetOperations = [];
|
113
|
+
const calendarOperations = [];
|
114
|
+
|
90
|
-
// メンバー
|
115
|
+
// メンバーごとに処理
|
91
|
-
//
|
92
|
-
members.forEach(m => {
|
116
|
+
members.forEach(member => {
|
93
|
-
// メンバー名と同じ名前の転記先シートを取得
|
117
|
+
// メンバー名と同じ名前の転記先シートを取得または作成
|
94
|
-
let sheet = ss.getSheetByName(m.name);
|
118
|
+
let sheet = ss.getSheetByName(member.name);
|
95
119
|
if (!sheet) {
|
96
|
-
sheet = ss.insertSheet(m.name);
|
120
|
+
sheet = ss.insertSheet(member.name);
|
97
|
-
console.log(`${m.name}というシート
|
121
|
+
console.log(`${member.name}というシートを作成しました`);
|
98
122
|
}
|
99
123
|
|
100
|
-
// 説明欄にメンバー名がある予定
|
124
|
+
// 説明欄にメンバー名がある予定を抽出
|
101
|
-
const events = schedules.filter(e =>
|
125
|
+
const events = schedules.filter(e =>
|
126
|
+
e.description.includes(member.name)
|
127
|
+
);
|
102
128
|
|
129
|
+
// シート操作をバッチに追加
|
130
|
+
sheetOperations.push({
|
131
|
+
sheet: sheet,
|
132
|
+
member: member,
|
133
|
+
events: events
|
134
|
+
});
|
135
|
+
|
103
|
-
//
|
136
|
+
// カレンダー操作をバッチに追加
|
137
|
+
const memberCalendar = memberCalendars.get(member.id);
|
138
|
+
if (memberCalendar && events.length > 0) {
|
139
|
+
calendarOperations.push({
|
140
|
+
calendar: memberCalendar,
|
141
|
+
member: member,
|
142
|
+
events: events
|
143
|
+
});
|
144
|
+
}
|
145
|
+
|
146
|
+
});
|
147
|
+
|
148
|
+
// シート操作をバッチ実行
|
149
|
+
console.log('シート更新を開始...');
|
150
|
+
sheetOperations.forEach(op => {
|
104
|
-
if (events.length > 0) {
|
151
|
+
if (op.events.length > 0) {
|
105
|
-
// 事前に対象範囲を
|
152
|
+
// 事前に対象範囲をクリア
|
106
|
-
const lastRow = sheet.getLastRow();
|
153
|
+
const lastRow = op.sheet.getLastRow();
|
107
154
|
if (lastRow >= startRow) {
|
108
|
-
sheet.getRange(startRow, 1, lastRow, sheet.getLastColumn())
|
155
|
+
op.sheet.getRange(startRow, 1, lastRow, op.sheet.getLastColumn())
|
109
156
|
.clearContent();
|
110
157
|
}
|
111
158
|
|
112
|
-
//
|
159
|
+
// データを一括書き込み
|
113
|
-
const values = events
|
160
|
+
const values = op.events.map(e => [
|
161
|
+
e.start,
|
162
|
+
e.end,
|
114
|
-
|
163
|
+
e.description.replace(new RegExp(op.member.name, 'g'), '').trim()
|
115
|
-
sheet.getRange(startRow, 1, values.length, values[0].length)
|
116
|
-
|
164
|
+
]);
|
117
165
|
|
118
|
-
|
166
|
+
if (values.length > 0) {
|
119
|
-
|
167
|
+
op.sheet.getRange(startRow, 1, values.length, values[0].length)
|
168
|
+
.setValues(values);
|
169
|
+
}
|
170
|
+
}
|
171
|
+
});
|
120
172
|
|
121
|
-
|
173
|
+
// カレンダー操作をバッチ実行
|
174
|
+
console.log('カレンダー更新を開始...');
|
175
|
+
calendarOperations.forEach(op => {
|
176
|
+
// 既存の★付き予定を一括削除
|
122
|
-
|
177
|
+
const existingEvents = op.calendar.getEvents(today, nextMonthEnd);
|
178
|
+
const eventsToDelete = existingEvents.filter(e =>
|
179
|
+
e.getTitle().startsWith(eventMark)
|
180
|
+
);
|
123
181
|
|
124
|
-
|
182
|
+
// 削除を並列実行するために配列に集める
|
125
|
-
|
183
|
+
eventsToDelete.forEach(e => {
|
126
|
-
if (e.getTitle().startsWith(eventMark)) {
|
127
|
-
|
184
|
+
e.deleteEvent();
|
128
|
-
}
|
129
|
-
|
185
|
+
});
|
130
186
|
|
131
|
-
|
187
|
+
// 新しい予定を一括作成
|
132
|
-
|
188
|
+
op.events.forEach(e => {
|
133
|
-
|
189
|
+
op.calendar.createEvent(eventMark + e.title, e.start, e.end);
|
134
|
-
|
190
|
+
});
|
135
|
-
|
191
|
+
|
136
192
|
});
|
193
|
+
|
194
|
+
console.log('処理完了');
|
195
|
+
|
137
196
|
}
|
138
197
|
```
|
5
test
CHANGED
@@ -58,7 +58,7 @@
|
|
58
58
|
const eventMark = '★';
|
59
59
|
|
60
60
|
// 転記元カレンダーID
|
61
|
-
const fromCalendarID = 'xxxxxxx@g
|
61
|
+
const fromCalendarID = 'xxxxxxx@group.calendar.google.com'; // 個人カレンダー(元)
|
62
62
|
|
63
63
|
// メンバーの名前とカレンダーIDを記入したシート名
|
64
64
|
const idSheetName = 'IDマスタ';
|
4
test
CHANGED
@@ -58,7 +58,7 @@
|
|
58
58
|
const eventMark = '★';
|
59
59
|
|
60
60
|
// 転記元カレンダーID
|
61
|
-
const fromCalendarID = '
|
61
|
+
const fromCalendarID = 'xxxxxxx@gmail.com'; // 個人カレンダー(元)
|
62
62
|
|
63
63
|
// メンバーの名前とカレンダーIDを記入したシート名
|
64
64
|
const idSheetName = 'IDマスタ';
|
3
複数のメンバーの予定の転記に対応したコードの例を追記しました
test
CHANGED
@@ -47,3 +47,92 @@
|
|
47
47
|
```
|
48
48
|
|
49
49
|
今回は、エラーについてのみ回答しました
|
50
|
+
|
51
|
+
【以下は、参考までに複数のメンバーの予定の転記に対応したコードの例です】
|
52
|
+
|
53
|
+
```JavaScript
|
54
|
+
// IDマスタというシートに各メンバーの名前(A列) カレンダーID(B列)を記入しておく
|
55
|
+
// この関数を時間主導型のトリガーで深夜帯に実行するように設定しておく
|
56
|
+
function myFunction() {
|
57
|
+
// このスクリプトで転記した予定につける目印
|
58
|
+
const eventMark = '★';
|
59
|
+
|
60
|
+
// 転記元カレンダーID
|
61
|
+
const fromCalendarID = 'yellow.green.pointer@gmail.com'; // 個人カレンダー(元)
|
62
|
+
|
63
|
+
// メンバーの名前とカレンダーIDを記入したシート名
|
64
|
+
const idSheetName = 'IDマスタ';
|
65
|
+
|
66
|
+
// メンバーのシートへの記入開始位置
|
67
|
+
const startRow = 1;
|
68
|
+
|
69
|
+
// スプレッドシートを取得
|
70
|
+
const ss = SpreadsheetApp.getActiveSpreadsheet()
|
71
|
+
|
72
|
+
// メンバーごとの氏名とカレンダーIDをシートから取得して配列membersに保存
|
73
|
+
const idSheet = ss.getSheetByName(idSheetName);
|
74
|
+
const idValues = idSheet.getDataRange().getValues();
|
75
|
+
const members = idValues.map(v => ({ name: v[0], id: v[1] }));
|
76
|
+
|
77
|
+
// 登録元カレンダーから翌月末日までの予定の内容を配列schedulesに保存
|
78
|
+
const calendar = CalendarApp.getCalendarById(fromCalendarID);
|
79
|
+
const today = new Date();
|
80
|
+
const nextDay = new Date(today.getFullYear(), today.getMonth() + 2, 0);
|
81
|
+
const schedules = calendar.getEvents(today, nextDay)
|
82
|
+
.map(e => ({
|
83
|
+
title: e.getTitle(),
|
84
|
+
start: e.getStartTime(),
|
85
|
+
end: e.getEndTime(),
|
86
|
+
description: e.getDescription(),
|
87
|
+
}));
|
88
|
+
|
89
|
+
//
|
90
|
+
// メンバー(m)ごとに繰返し処理
|
91
|
+
//
|
92
|
+
members.forEach(m => {
|
93
|
+
// メンバー名と同じ名前の転記先シートを取得
|
94
|
+
let sheet = ss.getSheetByName(m.name);
|
95
|
+
if (!sheet) {
|
96
|
+
sheet = ss.insertSheet(m.name);
|
97
|
+
console.log(`${m.name}というシートが見つからなかったので作成しました`);
|
98
|
+
}
|
99
|
+
|
100
|
+
// 説明欄にメンバー名がある予定の内容を配列eventsに抽出
|
101
|
+
const events = schedules.filter(e => e.description.includes(m.name));
|
102
|
+
|
103
|
+
// メンバーの予定があったらシートに記入
|
104
|
+
if (events.length > 0) {
|
105
|
+
// 事前に対象範囲を消去
|
106
|
+
const lastRow = sheet.getLastRow();
|
107
|
+
if (lastRow >= startRow) {
|
108
|
+
sheet.getRange(startRow, 1, lastRow, sheet.getLastColumn())
|
109
|
+
.clearContent();
|
110
|
+
}
|
111
|
+
|
112
|
+
// eventsを二次元配列valuesに変換して一括記入[開始日時, 終了日時, 名前を除去した説明]
|
113
|
+
const values = events
|
114
|
+
.map(e => [e.start, e.end, e.description.replace(m.name, '')]);
|
115
|
+
sheet.getRange(startRow, 1, values.length, values[0].length)
|
116
|
+
.setValues(values);
|
117
|
+
|
118
|
+
// メンバーのカレンダーを取得
|
119
|
+
const mCalendar = CalendarApp.getCalendarById(m.id);
|
120
|
+
|
121
|
+
// メンバーのカレンダーから翌月末日までの予定を取得
|
122
|
+
const mSchedules = mCalendar.getEvents(today, nextDay);
|
123
|
+
|
124
|
+
// 既存の予定の中で目印が付いた予定は削除しておく
|
125
|
+
mSchedules.forEach(e => {
|
126
|
+
if (e.getTitle().startsWith(eventMark)) {
|
127
|
+
e.deleteEvent();
|
128
|
+
}
|
129
|
+
});
|
130
|
+
|
131
|
+
// 予定を再登録(タイトルに目印を付ける)
|
132
|
+
events.forEach(e => {
|
133
|
+
mCalendar.createEvent(eventMark + e.title, e.start, e.end);
|
134
|
+
});
|
135
|
+
};
|
136
|
+
});
|
137
|
+
}
|
138
|
+
```
|
2
補足
test
CHANGED
@@ -45,3 +45,5 @@
|
|
45
45
|
// 予定を再登録(タイトルに★を付ける)
|
46
46
|
for (var i = RANGE; i <= sheet.getLastRow(); i++) {
|
47
47
|
```
|
48
|
+
|
49
|
+
今回は、エラーについてのみ回答しました
|
1
説明を修正
test
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
スプレッドシートへの出力する際に
|
1
|
+
スプレッドシートへの出力をする際に
|
2
2
|
range(出力する行番号)を予定ごとに更新しているので、
|
3
3
|
説明に「佐藤」が含まれない予定があるとその行が空行になります。
|
4
4
|
|
@@ -7,8 +7,9 @@
|
|
7
7
|
|
8
8
|
回避方法としては、
|
9
9
|
例えば次のコードのように
|
10
|
+
繰返し処理の前にrange(出力する行番号)の初期値をRANGE(記入開始行)に設定しておき、
|
10
|
-
スプレッドシート
|
11
|
+
繰返し処理の中でスプレッドシートに出力する際には
|
11
|
-
説明に「佐藤」が含まれる予定の時だけrange
|
12
|
+
説明に「佐藤」が含まれる予定の時だけrangeの更新をするようにします
|
12
13
|
|
13
14
|
|
14
15
|
```JavaScript
|
@@ -29,7 +30,7 @@
|
|
29
30
|
```
|
30
31
|
|
31
32
|
なお、
|
32
|
-
予定の
|
33
|
+
予定のスプレッドシートへの出力の際に
|
33
34
|
開始行をRANGEで指定していたのですから
|
34
35
|
```JavaScript
|
35
36
|
// 予定を再登録(タイトルに★を付ける)
|
@@ -38,7 +39,7 @@
|
|
38
39
|
|
39
40
|
のところは i = 1ではなく
|
40
41
|
次のように i = RANGEとしておかないと
|
41
|
-
RANGEを1以外にしたときにも1行目から再登録しようとしてエラーが発生します
|
42
|
+
RANGE(記入開始行)を1以外にしたときにも1行目から再登録しようとしてエラーが発生します
|
42
43
|
|
43
44
|
```JavaScript
|
44
45
|
// 予定を再登録(タイトルに★を付ける)
|