-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathModule1-Submissions.js
245 lines (200 loc) · 10.6 KB
/
Module1-Submissions.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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
// Module1.gs
// Request Submissions: sends emails, sets up the new mailing, and reminder trigger
function requestSubmissionsModule() {
Logger.log('Request Submissions Module started.');
// Update form titles with the current reporting month
updateFormTitlesWithCurrentReportingMonth();
Logger.log('Form titles updated with the current reporting month.');
// Get the universal spreadsheet time zone
const spreadsheetTimeZone = getProjectTimeZone();
Logger.log(`Spreadsheet time zone: ${spreadsheetTimeZone}`);
const registrySheet = SpreadsheetApp.openById(AMBASSADOR_REGISTRY_SPREADSHEET_ID).getSheetByName(REGISTRY_SHEET_NAME); // Open the "Registry" sheet
Logger.log('Opened "Registry" sheet from "Ambassador Registry" spreadsheet.');
// Fetch data from Registry sheet (Emails and Status)
const registryEmailColIndex = getColumnIndexByName(registrySheet, AMBASSADOR_EMAIL_COLUMN);
const registryAmbassadorStatus = getColumnIndexByName(registrySheet, AMBASSADOR_STATUS_COLUMN);
const registryAmbassadorDiscordHandle = getColumnIndexByName(registrySheet, AMBASSADOR_DISCORD_HANDLE_COLUMN);
const registryData = registrySheet
.getRange(2, 1, registrySheet.getLastRow() - 1, registrySheet.getLastColumn())
.getValues(); // Fetch Emails, Discord Handles, and Status
Logger.log(`Fetched data from "Registry" sheet: ${JSON.stringify(registryData)}`);
// Filter out ambassadors with 'Expelled' in their status
const eligibleEmails = registryData
.filter((row) => !row[registryAmbassadorStatus - 1].includes('Expelled')) // Exclude expelled ambassadors
.map((row) => [row[registryEmailColIndex - 1], row[registryAmbassadorDiscordHandle - 1]]); // Extract only emails
Logger.log(`Eligible ambassadors emails: ${JSON.stringify(eligibleEmails)}`);
// Get deliverable date (previous month date)
const deliverableDate = getPreviousMonthDate(spreadsheetTimeZone); // Call from SharedUtilities.gs
Logger.log(`Deliverable date: ${deliverableDate}`);
const month = Utilities.formatDate(deliverableDate, spreadsheetTimeZone, 'MMMM'); // Format the deliverable date to get the month name
const year = Utilities.formatDate(deliverableDate, spreadsheetTimeZone, 'yyyy'); // Format the deliverable date to get the year
Logger.log(`Formatted month and year: ${month} ${year}`);
// Calculate the exact deadline date based on submission window
const submissionWindowStart = new Date();
const submissionDeadline = new Date(submissionWindowStart.getTime() + SUBMISSION_WINDOW_MINUTES * 60 * 1000); // Convert minutes to milliseconds
const submissionDeadlineDate = Utilities.formatDate(submissionDeadline, spreadsheetTimeZone, 'MMMM dd, yyyy');
eligibleEmails.forEach((row) => {
const email = row[0]; //
const discordHandle = row[1]; // Get Discord Handle from Registry
// Validating email
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; // Simple email regex
if (!emailRegex.test(email)) {
const warningMessage = `Warning: Invalid or missing email for Discord Handle "${discordHandle}". Skipping.`;
Logger.log(warningMessage);
return; // Skip invalid emails
}
if (!discordHandle) {
Logger.log(`Error: Discord handle not found for email: ${email}`);
return; // Skip if Discord Handle is missing
}
// Composing email body
const message = REQUEST_SUBMISSION_EMAIL_TEMPLATE.replace('{AmbassadorDiscordHandle}', discordHandle)
.replace('{Month}', month)
.replace('{Year}', year)
.replace('{SubmissionFormURL}', SUBMISSION_FORM_URL)
.replace('{SUBMISSION_DEADLINE_DATE}', submissionDeadlineDate); // Insert deadline date
Logger.log(`Email message created for ${email} with Discord handle: ${discordHandle}`);
// Email sending logic
if (SEND_EMAIL) {
try {
MailApp.sendEmail({
to: email,
subject: '☑️Request for Submission',
htmlBody: message, // Use htmlBody to send HTML email
});
Logger.log(`Email sent to ${email}`);
} catch (error) {
Logger.log(`Failed to send email to ${email}. Error: ${error}`);
}
} else {
Logger.log(`Testing mode: Submission request email logged for ${email}`);
}
});
// Save the submission window start time in Los Angeles time zone format
setSubmissionWindowStart(submissionWindowStart);
// Set a trigger to check for non-respondents and send reminders
setupSubmissionReminderTrigger(submissionWindowStart);
Logger.log('Request Submissions completed.');
}
// Function to set up submission reminder trigger
function setupSubmissionReminderTrigger(submissionStartTime) {
Logger.log('Setting up submission reminder trigger.');
// Remove existing triggers for 'checkNonRespondents'
const triggers = ScriptApp.getProjectTriggers();
triggers.forEach((trigger) => {
if (trigger.getHandlerFunction() === 'checkNonRespondents') {
ScriptApp.deleteTrigger(trigger);
Logger.log('Existing reminder trigger removed.');
}
});
// Calculate the time for the reminder
const triggerDate = new Date(submissionStartTime.getTime() + SUBMISSION_WINDOW_REMINDER_MINUTES * 60 * 1000);
if (isNaN(triggerDate.getTime())) {
Logger.log('Invalid Date for trigger.');
return;
}
// Create the reminder trigger
ScriptApp.newTrigger('checkNonRespondents').timeBased().at(triggerDate).create();
Logger.log('Reminder trigger created.');
}
// Check for non-respondents by comparing 'Form Responses' and 'Registry' sheets based on the submission window
function checkNonRespondents() {
Logger.log('Checking for non-respondents.');
// Retrieve submission window start time
const submissionWindowStartStr = PropertiesService.getScriptProperties().getProperty('submissionWindowStart');
if (!submissionWindowStartStr) {
Logger.log('Submission window start time not found.');
return;
}
// Calculate submission window start and end times
const submissionWindowStart = new Date(submissionWindowStartStr);
const submissionWindowEnd = new Date(submissionWindowStart.getTime() + SUBMISSION_WINDOW_MINUTES * 60 * 1000);
// Open Registry and Form Responses sheets
const registrySheet = SpreadsheetApp.openById(AMBASSADOR_REGISTRY_SPREADSHEET_ID).getSheetByName(REGISTRY_SHEET_NAME);
const formResponseSheet = getSubmissionFormResponseSheet();
Logger.log('Sheets successfully fetched.');
// Get column indices for required headers
const registryEmailColIndex = getColumnIndexByName(registrySheet, AMBASSADOR_EMAIL_COLUMN);
const registryAmbassadorStatusColIndex = getColumnIndexByName(registrySheet, AMBASSADOR_STATUS_COLUMN);
const responseEmailColIndex = getColumnIndexByName(formResponseSheet, SUBM_FORM_USER_PROVIDED_EMAIL_COLUMN);
const responseTimestampColIndex = getColumnIndexByName(formResponseSheet, GOOGLE_FORM_TIMESTAMP_COLUMN);
// Fetch registry data and filter eligible emails
const registryData = registrySheet
.getRange(2, 1, registrySheet.getLastRow() - 1, registrySheet.getLastColumn())
.getValues();
const eligibleEmails = registryData
.filter((row) => !row[registryAmbassadorStatusColIndex - 1].includes('Expelled'))
.map((row) => row[registryEmailColIndex - 1]);
Logger.log(`Eligible emails: ${eligibleEmails}`);
// Fetch form responses
const responseData = formResponseSheet
.getRange(2, 1, formResponseSheet.getLastRow() - 1, formResponseSheet.getLastColumn())
.getValues();
// Filter valid responses within submission window
const validResponses = responseData.filter((row) => {
const timestamp = new Date(row[responseTimestampColIndex - 1]);
return timestamp >= submissionWindowStart && timestamp <= submissionWindowEnd;
});
const respondedEmails = validResponses.map((row) => row[responseEmailColIndex - 1]);
Logger.log(`Responded emails: ${respondedEmails}`);
// Identify non-respondents
const nonRespondents = eligibleEmails.filter((email) => !respondedEmails.includes(email));
Logger.log(`Non-respondents: ${nonRespondents}`);
// Send reminders to non-respondents
if (nonRespondents.length > 0) {
sendReminderEmails(nonRespondents);
Logger.log(`Reminders sent to ${nonRespondents.length} non-respondents.`);
} else {
Logger.log('No non-respondents found.');
}
}
// Function for sending reminder emails with logging
function sendReminderEmails(nonRespondents) {
Logger.log('Sending reminder emails.');
const registrySheet = SpreadsheetApp.openById(AMBASSADOR_REGISTRY_SPREADSHEET_ID).getSheetByName(REGISTRY_SHEET_NAME); // Open the "Registry" sheet
Logger.log('Opened "Registry" sheet.');
if (!nonRespondents || nonRespondents.length === 0) {
Logger.log('No non-respondents found.');
return; // Exit if there are no non-respondents
}
// Dynamically fetch column indices
const registryEmailColIndex = getColumnIndexByName(registrySheet, AMBASSADOR_EMAIL_COLUMN); // Email column index
const registryDiscordHandleColIndex = getColumnIndexByName(registrySheet, AMBASSADOR_DISCORD_HANDLE_COLUMN); // Discord Handle column index
// Validate column indices
if (registryEmailColIndex === -1 || registryDiscordHandleColIndex === -1) {
Logger.log('Error: One or more required columns not found in Registry sheet.');
return;
}
nonRespondents.forEach((email) => {
// Validate email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; // Simple regex for validating email
if (!emailRegex.test(email)) {
Logger.log(`Warning: Invalid email "${email}". Skipping.`);
return; // Skip invalid or empty email
}
// Find the row with the given email in the Registry
const result = registrySheet.createTextFinder(email).findNext();
if (result) {
const row = result.getRow(); // Get the row number
Logger.log(`Non-respondent found at row: ${row}`);
// Fetch Discord Handle dynamically
const discordHandle = registrySheet.getRange(row, registryDiscordHandleColIndex).getValue();
Logger.log(`Discord handle found for ${email}: ${discordHandle}`);
// Create the reminder email message
const message = REMINDER_EMAIL_TEMPLATE.replace('{AmbassadorDiscordHandle}', discordHandle);
// Send the email
if (SEND_EMAIL) {
try {
MailApp.sendEmail(email, '🕚 Reminder to Submit', message); // Send the reminder email
Logger.log(`Reminder email sent to: ${email} (Discord: ${discordHandle})`);
} catch (error) {
Logger.log(`Failed to send reminder email to ${email}. Error: ${error}`);
}
} else {
Logger.log(`Testing mode: Reminder email logged for ${email}`);
}
} else {
Logger.log(`Error: Could not find the ambassador with email ${email}`);
}
});
}