-
Notifications
You must be signed in to change notification settings - Fork 0
/
sheet_to_cal.js
227 lines (206 loc) · 6.42 KB
/
sheet_to_cal.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
const titleRowMap = {
year: 'Year',
month: 'Month',
name: 'Name',
units: 'Units',
goal: 'Goal',
reached: 'Reached',
id: 'id'
};
const titleRowKeys = ['year', 'month', 'name', 'units', 'goal', 'reached', 'id'];
// Adds the custom menu to the active spreadsheet.
function onOpen() {
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const menuEntries = [
{
name: 'Update to Calendar Tasks',
functionName: 'syncToCalendar'
}
];
spreadsheet.addMenu('Calendar Sync', menuEntries);
}
// Responds do HTTP GET request calling the syncToSheet method
// function doGet() {
// return ContentService.createTextOutput('Hello, world!');
// }
// Creates a mapping array between spreadsheet column and event field name
function createIdxMap(row) {
const idxMap = [];
for (let idx = 0; idx < row.length; idx++) {
const fieldFromHdr = row[idx];
for (const titleKey in titleRowMap) {
if (titleRowMap[titleKey] == fieldFromHdr) {
idxMap.push(titleKey);
break;
}
}
if (idxMap.length <= idx) {
// Header field not in map, so add null
idxMap.push(null);
}
}
return idxMap;
}
// Converts a spreadsheet row into an object containing event-related fields
function reformatGoal(row, idxMap, keysToAdd) {
const reformatted = row.reduce(function(event, value, idx) {
if (idxMap[idx] != null) {
event[idxMap[idx]] = value;
}
return event;
}, {});
for (const k in keysToAdd) {
reformatted[keysToAdd[k]] = '';
}
return reformatted;
}
// Returns list of fields that aren't in spreadsheet
function missingFields(idxMap) {
return titleRowKeys.filter(function(val) {
return idxMap.indexOf(val) < 0;
});
}
// Display error alert
function errorAlert(msg, evt, ridx) {
const ui = SpreadsheetApp.getUi();
if (evt) {
ui.alert(`Skipping row: ${msg} in event "${evt.title}", row ${ridx + 1}`);
} else {
ui.alert(msg);
}
}
// Given a date string ('yyyy-mm-dd') it returns an array of sundays (Date obj) in the given month
function sundaysInMonth(date) {
const sundays = [];
const arr = date.split('-').map(num => parseInt(num, 10));
const d = new Date(arr);
arr[1] += 1;
const nextMonth = new Date(arr);
for (d; d < nextMonth; d.setDate(d.getDate() + 1)) {
if (d.getDay() === 0) {
const sunday = new Date(d.toString());
sundays.push(`${sunday.getFullYear()}-${sunday.getMonth() + 1}-${sunday.getDate()}`);
}
}
return sundays;
}
// Creates Goal Hash: { monthA: { goals... }, monthB: { goals... }, ...}
function monthlyGoalMapper(data) {
// Map headers to indices
const idxMap = createIdxMap(data[0]);
const keysToAdd = missingFields(idxMap);
// Loop through sheet rows and create goalMap (hash of goals consolidated by string date keys that are the 1st of the month)
const goalMap = {};
for (let ridx = 1; ridx < data.length; ridx++) {
const sheetGoal = reformatGoal(data[ridx], idxMap, keysToAdd);
sheetGoal.rowId = ridx;
const date = `${sheetGoal.year}-${`0${sheetGoal.month}`.slice(-2)}-01`;
if (goalMap[date] === undefined) goalMap[date] = [];
goalMap[date].push(sheetGoal);
}
return goalMap;
}
// Creates hash with sundays (Date 'yyyy-mm-dd' string) as keys and list of weekly goals (strings) as values.
function goalBreakdown(data) {
const goalMap = monthlyGoalMapper(data);
const sundays = {};
const weeklyGoals = {};
Object.keys(goalMap).forEach(month => {
sundays[month] = sundaysInMonth(month);
sundays[month].forEach(sunday => {
const listOfWeeklyGoals = [];
goalMap[month].forEach(entry => {
listOfWeeklyGoals.push({
name: entry.name,
goal: entry.goal / sundays[month].length,
units: entry.units,
completed: false,
week_sunday: sunday,
month: entry.month
});
});
// add weekly events
weeklyGoals[sunday] = listOfWeeklyGoals;
});
});
return weeklyGoals;
}
function getTaskLists() {
const taskLists = Tasks.Tasklists.list().getItems();
if (!taskLists) {
return [];
}
return taskLists.map(function(taskList) {
return {
id: taskList.getId(),
name: taskList.getTitle()
};
});
}
// (date: datetime Obj)
function addTask(title, date, notes, taskList) {
const task = {
title,
due: date.toISOString(),
notes
};
Tasks.Tasks.insert(task, taskList.id);
}
// ---------------------- Synchronize from spreadsheet to calendar ----------------------
function syncToCalendar() {
// Get spreadsheet and data
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Goals');
const range = sheet.getDataRange();
const data = range.getValues();
if (data.length < 2) {
errorAlert('Spreadsheet must have a title row and at least one data row');
return;
}
const sheetLog = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('logger'); // logger
// Breakdown monthly goals into weekly goals
const weeklyGoals = goalBreakdown(data);
// Log goals in logger
sheetLog.getRange('D1').setValue('Sunday');
sheetLog.getRange('E1').setValue('weeklyGoals');
sheetLog.getRange('F1').setValue('InTasks?');
Object.keys(weeklyGoals).forEach((sunday, index) => {
sheetLog.getRange(`D${index + 2}`).setValue(sunday);
sheetLog.getRange(`E${index + 2}`).setValue(JSON.stringify(weeklyGoals[sunday]));
});
// Create calendar Tasks
let taskList = getTaskLists().find(list => list.name === 'Goals');
if (taskList === undefined) {
taskList = Tasks.Tasklists.insert({ id: 'goals', title: 'Goals' });
}
Object.keys(weeklyGoals).forEach((sunday, index) => {
weeklyGoals[sunday].forEach(goal => {
Utilities.sleep(500);
addTask(
`${goal.name}: ${goal.goal}${goal.units} `,
new Date(sunday),
'you can do it!',
taskList
);
});
sheetLog.getRange(`F${index + 2}`).setValue('true');
});
}
function temp() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('GoalsTemp');
const range = sheet.getDataRange();
const data = range.getValues();
const goalMap = monthlyGoalMapper(data);
Logger.log(JSON.stringify(goalMap));
const breaker = true;
if (breaker) return;
const taskList = getTaskLists().find(list => list.name === 'Goals');
Object.keys(goalMap).forEach(entry => {
Utilities.sleep(500);
addTask(
`${entry.name}: ${entry.goal}${entry.units} `,
new Date('2020-03-24'),
'you can do it!',
taskList
);
});
}