Bulk Update users G-suite - google-apps-script

I'm about to bulk update multiple users email domaine in g-suite using the code below - found it here https://support.google.com/a/answer/7068037
// Cell Colors
COLOR_GRAY = '#434343';
COLOR_GREEN = '#B5D5A7';
COLOR_RED = '#E89898';
COLOR_YELLOW = '#FDE398';
COLOR_WHITE = '#FFFFFF';
// Sheet Columns
COLUMN_PARAMETER = 1;
COLUMN_RESULT = 2;
COLUMN_USER = 1;
COLUMN_VALUE = 2;
// Header Labels
HEADER_PARAMETER = 'Parameter';
HEADER_RESULT = 'Result';
HEADER_USER = 'Username';
HEADER_VALUE = 'Value';
// Status Messages
MESSAGE_COMPLETE = 'User renamed to: ';
MESSAGE_PAUSE = 'Select "Start Rename" from the "Actions" menu to continue';
MESSAGE_RUNTIME = 'Runtime exceeded. Will resume automatically in a few minutes';
MESSAGE_STOP = 'Select "Start Rename" from the "Actions" menu to continue';
// Parameter Labels
PARAMETER_DOMAIN = 'Domain Name';
PARAMETER_CURRENT = 'Current Row';
PARAMETER_RUN = 'Run Number';
// Parameter Dimensions
PARAMETER_COLUMNS = 2;
PARAMETER_ROWS = 4;
// Result Dimensions
RESULT_COLUMNS = 2;
// Sheet Rows
ROW_DOMAIN = 2;
ROW_HEADER = 1;
ROW_CURRENT = 4;
ROW_RUN = 3;
// Runtime Settings
RUNTIME_MAX = 270000;
RUNTIME_PAUSE = 60000;
// Sheet Names
SHEET_PARAMETERS = 'Parameters';
SHEET_RESULTS = 'Results';
// Status Labels
STATUS_ABORT = '[ABORTED]';
STATUS_ERROR = '[ERROR]';
STATUS_PAUSE = '[PAUSED]';
STATUS_COMPLETE = '[COMPLETED]';
// Cell Sizes
WIDTH_SMALL = 200;
WIDTH_MEDIUM = 400;
WIDTH_LARGE = 800;
// Parameter Values
VALUE_CURRENT = 2;
VALUE_NULL = '';
VALUE_RUN = 1;
// Clears all triggers from current project.
function clearTriggers() {
var triggers = ScriptApp.getProjectTriggers();
for (var i in triggers) {
ScriptApp.deleteTrigger(triggers[i]);
}
}
// Returns currently active sheet
function getCurrentSheet() {
return SpreadsheetApp.getActiveSheet();
}
// Returns currently active spreadsheet
function getCurrentSpreadsheet() {
return SpreadsheetApp.getActiveSpreadsheet();
}
// Returns value of specified cell in a sheet
function getCellValue(sheet, row, column) {
return sheet.getRange(row, column).getValue();
}
// Returns value of the specified parameter
function getParameter(parameter) {
return getCellValue(getSheet(SHEET_PARAMETERS), parameter, COLUMN_VALUE);
}
// Returns sheet with the specified name (if one exists)
function getSheet(name) {
return getCurrentSpreadsheet().getSheetByName(name);
}
// Returns true if 'haystack' contains 'needle'.
function hasString(haystack, needle) {
return String(haystack).indexOf(needle) != -1;
}
// Returns true if script is nearing the max run time (4.5 min).
function isRuntimeExpired(start, currentTime) {
return currentTime - start >= RUNTIME_MAX;
}
// Adds custom items to "Actions" menu of spreadsheet
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Actions')
.addItem('Start Rename', 'startRename')
.addItem('Pause Rename', 'pauseRename')
.addItem('Stop Rename', 'stopRename')
.addItem('Reset Spreadsheet', 'resetSpreadsheet')
.addToUi();
}
// Pause rename process (manually)
function pauseRename() {
clearTriggers();
var results = getSheet(SHEET_RESULTS);
var lastRow = results.getLastRow();
var row = getParameter(ROW_CURRENT);
var currentResult = getCellValue(results, row, COLUMN_RESULT);
if (!hasString(currentResult, STATUS_ABORT) &&
!hasString(currentResult, STATUS_ERROR) &&
!hasString(currentResult, STATUS_PAUSE)) {
row = row + 1;
}
if (row < lastRow) {
setResult(row, COLOR_YELLOW, STATUS_PAUSE, MESSAGE_PAUSE);
}
}
// Formats layout of "Parameters" sheet
function formatParameters() {
var parameters = getSheet(SHEET_PARAMETERS);
var lastColumn = parameters.getMaxColumns();
var lastRow = parameters.getMaxRows();
if (lastColumn > PARAMETER_COLUMNS) {
parameters.deleteColumns(
PARAMETER_COLUMNS + 1, lastColumn - PARAMETER_COLUMNS);
}
parameters.getRange(ROW_HEADER, COLUMN_PARAMETER, lastRow).clear();
parameters.getRange(ROW_HEADER, COLUMN_VALUE, lastRow).clear();
parameters.getRange(ROW_HEADER, COLUMN_RESULT).setFontColor(COLOR_WHITE);
parameters.setColumnWidth(COLUMN_PARAMETER, WIDTH_SMALL);
parameters.setColumnWidth(COLUMN_VALUE, WIDTH_SMALL);
parameters.getRange(ROW_HEADER, COLUMN_PARAMETER)
.setBackground(COLOR_GRAY)
.setFontColor(COLOR_WHITE)
.setValue(HEADER_PARAMETER);
parameters.getRange(ROW_HEADER, COLUMN_VALUE)
.setBackground(COLOR_GRAY)
.setFontColor(COLOR_WHITE)
.setValue(HEADER_VALUE);
parameters.getRange(ROW_DOMAIN, COLUMN_PARAMETER).setValue(PARAMETER_DOMAIN);
parameters.getRange(ROW_RUN, COLUMN_PARAMETER).setValue(PARAMETER_RUN);
parameters.getRange(ROW_CURRENT, COLUMN_PARAMETER)
.setValue(PARAMETER_CURRENT);
parameters.setFrozenRows(ROW_HEADER);
}
// Formats layout of "Results" sheet
function formatResults() {
var results = getSheet(SHEET_RESULTS);
var userHeader = results.getRange(ROW_HEADER, COLUMN_USER);
var lastColumn = results.getMaxColumns();
var lastRow = results.getLastRow();
if (hasString(userHeader.getValue(), '#')) {
results.insertRowBefore(1);
}
if (lastColumn > RESULT_COLUMNS) {
results.deleteColumns(RESULT_COLUMNS + 1, lastColumn - RESULT_COLUMNS);
}
if (lastColumn < RESULT_COLUMNS) {
results.insertColumnAfter(COLUMN_USER);
}
results.getRange(ROW_HEADER, COLUMN_USER, lastRow).clearFormat();
results.getRange(ROW_HEADER, COLUMN_RESULT, lastRow).clear();
userHeader.setFontColor(COLOR_WHITE);
results.getRange(ROW_HEADER, COLUMN_RESULT).setFontColor(COLOR_WHITE);
userHeader.setValue(HEADER_USER);
results.setColumnWidth(COLUMN_USER, WIDTH_MEDIUM);
results.setColumnWidth(COLUMN_RESULT, WIDTH_LARGE);
results.setFrozenRows(ROW_HEADER);
setResult(ROW_HEADER, COLOR_GRAY, HEADER_RESULT, '');
}
// Renames email address of each user in "Results" sheet
function renameUsers(start) {
var results = getSheet(SHEET_RESULTS);
SpreadsheetApp.setActiveSheet(results);
var lastRow = results.getLastRow();
var firstRow = getParameter(ROW_CURRENT);
var domain = getParameter(ROW_DOMAIN);
if (firstRow && firstRow <= lastRow) {
setResult(firstRow, COLOR_WHITE, VALUE_NULL, VALUE_NULL);
for (var row = firstRow; row <= lastRow; row++) {
setCurrentRow(row);
var currentEmail = getCellValue(results, row, COLUMN_USER);
var currentResult = getCellValue(results, row, COLUMN_RESULT);
var userExists = false;
if (row != lastRow) {
if (isRuntimeExpired(start, new Date().getTime())) {
setTrigger(RUNTIME_PAUSE);
setTrigger(RUNTIME_PAUSE + RUNTIME_MAX);
setResult(row, COLOR_YELLOW, STATUS_PAUSE, MESSAGE_RUNTIME);
return;
}
}
if (hasString(currentResult, STATUS_PAUSE)) {
return;
}
if (hasString(currentResult, STATUS_ABORT)) {
var parameters = getSheet(SHEET_PARAMETERS);
setCurrentRow(row);
getCurrentSpreadsheet().deleteSheet(parameters);
return;
}
if (hasString(currentResult, STATUS_COMPLETE)) continue;
setResult(row, COLOR_RED, STATUS_ABORT, VALUE_NULL);
if (!hasString(currentEmail, '#')) continue;
var delimiter = currentEmail.indexOf('#');
var currentUsername = currentEmail.substring(0, delimiter);
var newUsername = currentUsername + '#' + domain;
var user = {'primaryEmail': newUsername};
try {
AdminDirectory.Users.update(user, currentEmail);
message = MESSAGE_COMPLETE + user.primaryEmail;
setResult(row, COLOR_GREEN, STATUS_COMPLETE, message);
} catch (err) {
setResult(row, COLOR_RED, STATUS_ERROR, err.message);
}
Utilities.sleep(100);
parameters = getSheet(SHEET_PARAMETERS);
}
}
getCurrentSpreadsheet().deleteSheet(parameters);
return;
}
//Resets layout of "Results" sheet; removes "Parameters" sheet
function resetSpreadsheet() {
clearTriggers();
var parameters = getSheet(SHEET_PARAMETERS);
if (parameters) {
getCurrentSpreadsheet().deleteSheet(parameters);
}
formatResults();
}
//Sets background color of specified cell in a sheet
function setCellColor(sheet, row, column, color) {
sheet.getRange(row, column).setBackground(color);
}
//Sets value of specified cell in a sheet
function setCellValue(sheet, row, column, value) {
sheet.getRange(row, column).setValue(value);
}
//Sets value of "Row Number" parameter
function setCurrentRow(row) {
var parameters = getSheet(SHEET_PARAMETERS);
Utilities.sleep(50);
setCellValue(parameters, ROW_CURRENT, COLUMN_VALUE, row);
Utilities.sleep(50);
}
//Sets value of "Domain Name" parameter
function setDomainName(domain) {
var parameters = getSheet(SHEET_PARAMETERS);
setCellValue(parameters, ROW_DOMAIN, COLUMN_VALUE, domain);
}
//Sets result of specified row in "Results" sheet
function setResult(row, color, status, message) {
var results = getSheet(SHEET_RESULTS);
results.getRange(row, COLUMN_USER).setBackground(color);
results.getRange(row, COLUMN_RESULT).setBackground(color);
if (message == VALUE_NULL) {
results.getRange(row, COLUMN_RESULT).setValue(status);
} else {
results.getRange(row, COLUMN_RESULT).setValue(status + ' ' + message);
}
}
//Sets value of "Run Number" parameter
function setRunNumber(count) {
var parameters = getSheet(SHEET_PARAMETERS);
setCellValue(parameters, ROW_RUN, COLUMN_VALUE, count);
}
function setTrigger(pause) {
ScriptApp.newTrigger('startRename').timeBased().after(pause).create();
}
//Starts rename process
function startRename() {
clearTriggers();
var start = new Date().getTime();
if (getSheet(SHEET_PARAMETERS)) {
var results = getSheet(SHEET_RESULTS);
var parameters = getSheet(SHEET_PARAMETERS);
setRunNumber(getParameter(ROW_RUN) + 1);
} else {
getCurrentSheet().setName(SHEET_RESULTS);
formatResults();
var domains = AdminDirectory.Domains.list('my_customer').domains;
var ui = SpreadsheetApp.getUi();
var input = ui.prompt('Rename users to which domain?', ui.ButtonSet.OK_CANCEL);
if (input.getSelectedButton() == ui.Button.OK) {
var valid = false;
var domain = input.getResponseText().toLowerCase();
for (var i in domains) {
if (domain == domains[i].domainName.toLowerCase()) {
valid = true;
}
}
if (!valid) {
ui.alert(
'The specified domain is invalid! Please try again.',
ui.ButtonSet.OK);
return;
}
} else {
return;
}
var results = getSheet(SHEET_RESULTS);
var parameters = getCurrentSpreadsheet().insertSheet(SHEET_PARAMETERS);
formatParameters();
setDomainName(domain);
setCurrentRow(VALUE_CURRENT);
setRunNumber(VALUE_RUN);
}
renameUsers(start);
}
//Stop rename process
function stopRename() {
clearTriggers();
var results = getSheet(SHEET_RESULTS);
var lastRow = results.getLastRow();
var row = getParameter(ROW_CURRENT);
var currentResult = getCellValue(results, row, COLUMN_RESULT);
if (hasString(currentResult, STATUS_ABORT) ||
hasString(currentResult, STATUS_ERROR) ||
hasString(currentResult, STATUS_PAUSE)) {
var parameters = getSheet(SHEET_PARAMETERS);
if (parameters) {
getCurrentSpreadsheet().deleteSheet(parameters);
}
} else {
row = row + 1;
}
setResult(row, COLOR_RED, STATUS_ABORT, MESSAGE_STOP);
}
Since I'm changing the domain I will like also to update different fields, like Department....
Is there a simplest way to do it rather then using a script?
Where do I find the users fields?
Regards
Hugo L

I wrote a script for managing G suite user email signatures which you might want to check out.
python set-signatures.py
It allows you to set the signature for every user in your organization using an HTML template, a CSV of users and the Google Service Account API.

Related

Apps Script trigger function keeps on running though the trigger has been deleted

I have created a trigger which will first execute after 5 minutes, the when the trigger function starts executing, the trigger is deleted at first place , then the required code executes, and then new trigger is created which executes in 1 minutes. Also I have a condition where in the trigger is deleted and never created.. but the trigger still continues to execute.
I get total_uploads value as 29393, but it ended up adding nearly 100k records.
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.createMenu('Fetch CoA')
.addItem('Fetch CoA', 'main')
.addToUi();
}
function main() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var config = ss.getSheetByName("Config");
var hostname = config.getRange(2, 2).getValue();
var authorization = config.getRange(3, 2).getValue();
var coaID = config.getRange(4, 2).getValue();
var documentProperties = PropertiesService.getDocumentProperties();
documentProperties.setProperties({
'hostname': hostname,
'authorization': authorization,
'coaID': coaID,
'offset': '0',
'is_last_set': 0
});
var coaSheet = ss.getSheetByName("COA");
if (coaSheet.getLastRow() > 1) {
coaSheet.deleteRows(1, coaSheet.getLastRow() - 1);
}
console.log("Getting CoA Validaton");
writeCOA();
}
function writeCOA() {
var documentProperties = PropertiesService.getDocumentProperties();
var hostname = documentProperties.getProperty('hostname');
var authorization = documentProperties.getProperty('authorization');
var coaID = documentProperties.getProperty('coaID');
var offset = parseInt(documentProperties.getProperty('offset'));
var is_last_set = parseInt(documentProperties.getProperty('is_last_set'));
var options = {
"method": "GET",
"headers": {
"Authorization": "Token token=" + authorization
}
};
var today = new Date();
console.log("Writing the uploads array");
var ss = SpreadsheetApp.getActiveSpreadsheet();
var coaSheet = ss.getSheetByName("COA");
coaSheet.getRange("A1:D1").setValues([["id", "account_number", "created_at", "updated_at"]]);
var rowNum = 2;
var uploads = [];
var next_page = true;
var total_uploads = "...";
console.log("Getting all Account numbers for CoA: " + coaID);
while (next_page) {
documentProperties.setProperty('next_page', next_page);
if (isTimeUp(today) && is_last_set == 0) {
console.log("RepeatWriteCoA triggered");
// schedule a trigger for a different function
ScriptApp.newTrigger("repeatWriteCoA")
.timeBased()
.everyMinutes(5)
.create();
break;
}
console.log(`Getting uploads ${offset} to ${offset + 500} of ${total_uploads}`);
var platformUrl = `${hostname}/api/v3/chart_of_accounts/${coaID}/account_numbers?limit=500&offset=${offset}`;
//console.log(platformUrl);
var response = UrlFetchApp.fetch(platformUrl, options);
var jsonResponse = response.getContentText();
var page_of_uploads = JSON.parse(jsonResponse);
total_uploads = page_of_uploads['meta']['total'];
documentProperties.setProperty('total_uploads', total_uploads);
uploads = page_of_uploads['account_numbers'];
try {
offset = page_of_uploads['meta']['next']['offset'];
documentProperties.setProperty('offset', offset);
} catch (Exception) {
next_page = false;
documentProperties.setProperty('next_page', next_page);
console.log("Exception in getting the next page offset hence stopping the loop");
deleteTriggers();
// For last set of records that needs to added in our spreadsheet
console.log("Adding Last set of Records..");
for (i = 0; i < uploads.length; i++) {
acc_number = uploads[i].account_number.replace(/[^a-zA-Z0-9\s\p{P}]/g, '-');
coaSheet.getRange("A" + rowNum + ":D" + rowNum).setValues([[uploads[i].id, acc_number, uploads[i].created_at, uploads[i].updated_at]]);
rowNum += 1;
documentProperties.setProperty('rowNum', rowNum);
}
console.log("End of Last set of Records..");
is_last_set = 1;
}
console.log("Adding to spreadsheet");
for (i = 0; i < uploads.length; i++) {
acc_number = uploads[i].account_number.replace(/[^a-zA-Z0-9\s\p{P}]/g, '-');
coaSheet.getRange("A" + rowNum + ":D" + rowNum).setValues([[uploads[i].id, acc_number, uploads[i].created_at, uploads[i].updated_at]]);
rowNum += 1;
documentProperties.setProperty('rowNum', rowNum);
}
console.log("End of adding rows");
if (offset >= total_uploads) {
deleteTriggers();
}
}
documentProperties.setProperty('is_last_set', is_last_set);
}
function repeatWriteCoA() {
console.log("Triggered RepeatFunction... Running from Repeat function");
// Delete trigger , so that new function wont be trigger untill this function ends
deleteTriggers();
var documentProperties = PropertiesService.getDocumentProperties();
var hostname = documentProperties.getProperty('hostname');
var authorization = documentProperties.getProperty('authorization');
var coaID = documentProperties.getProperty('coaID');
var offset = parseInt(documentProperties.getProperty('offset'));
var next_page = documentProperties.getProperty('next_page');
var rowNum = parseInt(documentProperties.getProperty('rowNum'));
var total_uploads = parseInt(documentProperties.getProperty('total_uploads'));
var options = {
"method": "GET",
"headers": {
"Authorization": "Token token=" + authorization
}
};
var today = new Date();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var coaSheet = ss.getSheetByName("COA");
var is_last_set = parseInt(documentProperties.getProperty('is_last_set'));
console.log("Getting all Account numbers for CoA: " + coaID);
while (next_page) {
if (is_last_set == 1) {
console.log('last set already executed');
break;
}
console.log(`Getting uploads ${offset} to ${offset + 500} of ${total_uploads}`);
var platformUrl = `${hostname}/api/v3/chart_of_accounts/${coaID}/account_numbers?limit=500&offset=${offset}`;
var response = UrlFetchApp.fetch(platformUrl, options);
var jsonResponse = response.getContentText();
var page_of_uploads = JSON.parse(jsonResponse);
total_uploads = page_of_uploads['meta']['total'];
uploads = page_of_uploads['account_numbers'];
try {
offset = page_of_uploads['meta']['next']['offset'];
documentProperties.setProperty('offset', offset);
} catch (Exception) {
next_page = false;
documentProperties.setProperty('next_page', next_page);
console.log("Exception in getting the next page offset hence stopping the loop");
// For last set of records that needs to added in our spreadsheet
console.log("Adding Last set of Records..");
for (i = 0; i < uploads.length; i++) {
acc_number = uploads[i].account_number.replace(/[^a-zA-Z0-9\s\p{P}]/g, '-');
coaSheet.getRange("A" + rowNum + ":D" + rowNum).setValues([[uploads[i].id, acc_number, uploads[i].created_at, uploads[i].updated_at]]);
rowNum += 1;
documentProperties.setProperty('rowNum', rowNum);
}
console.log("End of Last set of Records..");
is_last_set = 1;
}
for (i = 0; i < uploads.length; i++) {
acc_number = uploads[i].account_number.replace(/[^a-zA-Z0-9\s\p{P}]/g, '-');
coaSheet.getRange("A" + rowNum + ":D" + rowNum).setValues([[uploads[i].id, acc_number, uploads[i].created_at, uploads[i].updated_at]]);
rowNum += 1;
documentProperties.setProperty('rowNum', rowNum);
}
// Creating new trigger so that it will trigger this function next time ,
// this time we are creating the trigger with 1 min
if (offset <= total_uploads && isTimeUp(today) && is_last_set == 0) {
console.log("RepeatWriteCoA triggered");
// schedule a trigger for a different function
ScriptApp.newTrigger("repeatWriteCoA")
.timeBased()
.everyMinutes(1)
.create();
break;
}
}
documentProperties.setProperty('is_last_set', is_last_set);
}
function isTimeUp(today) {
var now = new Date();
console.log(now.getTime() - today.getTime());
return now.getTime() - today.getTime() > 240000;
// 240000 = 4 mins; this is the threshold limit
// you are free to setup your own threshold limit
}
function deleteTriggers() {
// Loop over all triggers.
var allTriggers = ScriptApp.getProjectTriggers();
if (allTriggers.length > 0) {
console.log("Deleting trigger....");
for (var i = 0; i < allTriggers.length; i++) {
// If the current trigger is the correct one, delete it.
ScriptApp.deleteTrigger(allTriggers[i]);
}
}
}

Google script custom trigger onEdit(e) issue

I am getting error in line 29. But the same code works when pasted in another sheet. Separate trigger is used. Edit-> Current project's trigger.
var Team_email='abc#gmail.com';
function TriggerOnEdit(e)
{
Logger.log("stg1");
sendEmailOnassigned(e);
}
function showMessageOnApproval(e)
{
var edited_row = checkStatusIsAssigned(e);
if(edited_row > 0)
{
SpreadsheetApp.getUi().alert("Row # "+edited_row+" approved!");
}
}
function showMessageOnUpdate(e)
{
var range = e.range;
SpreadsheetApp.getUi().alert("range updated " + range.getA1Notation());
}
function checkStatusIsAssigned(e)
{
Logger.log("stg3");
var range = e.range;
if(range.getColumn() <= 12 &&
range.getLastColumn() >=12 )
{
var edited_row = range.getRow();
var status = SpreadsheetApp.getActiveSheet().getRange(edited_row,11).getValue();
if(status == 'Assigned')
{
return edited_row;
}
}
return 0;
}
function sendEmailOnassigned(e)
{
Logger.log("stg2");
var approved_row = checkStatusIsAssigned(e);
if(approved_row <= 0)
{
return;
}
Logger.log("stg4");
sendEmailByRow(approved_row);
}
function sendEmailByRow(row)
{
Logger.log("stg5");
var values = SpreadsheetApp.getActiveSheet().getRange(row,1,row,13).getValues();
var row_values = values[0];
var mail = composeAssignedEmail(row_values);
MailApp.sendEmail(Team_email,mail.subject,mail.message);
var Requester_email = composeRequesterEmail(row_values);
MailApp.sendEmail(Requester_email.email,Requester_email.subject,Requester_email.message);
}
function composeAssignedEmail(row_values)
{
Logger.log("stg6");
var Request_Number = row_values[1];
var email = row_values[2];
var Project_Name = row_values[4];
var Nature_Of_Work = row_values[5];
var Quantity = row_values[6];
var Estimated_Hours = row_values[7];
var Name = row_values[12];
var message = "Hello Requester, \n "+" \n The following Resource "+ Name ;
var subject = "Request_Number - " + Request_Number ;
return({message:message,subject:subject});
}
function composeRequesterEmail(row_values)
{
Logger.log("stg7");
var Request_Number = row_values[1];
var email = row_values[2] ;
var Project_Name = row_values[4];
var Nature_Of_Work = row_values[5];
var Quantity = row_values[6];
var Estimated_Hours = row_values[7];
var Name = row_values[12];
var message = "Hello Requester, \n "+" \n The following Resource "+ Name;
var subject = "Request_Number - " + Request_Number;
return({message:message,subject:subject, email:email });
}
Please tell me the error where is the mistake.
We can not use debug function to understand the mistake. because trigger is on edit.
Also when we use 'e' what are the information do we feed the function?
How to correct this?

Stuck at G Suite Developer Quickstart for "check student attendance in Google Meet courses"

I would be very appreciative of any input. Thank you;
I'm using the G Suite Developer Quickstart for a Meet attendance script
https://developers.google.com/gsuite/add-ons/editors/sheets/quickstart/attendance
Unfortunately, upon using the provided code, and having made adjustments, I either always get "Absent" or "Present" for all students (regardless of whether they were present or not) depending on if I use "null" of "false" in an if statement for activities for applicationName 'meet'.
Here is the code I'm using as a superuser:
...
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Opciones SG')
.addItem("Importar Curso", 'importCourses')
.addItem('Verificar Asistencia', 'checkAll')
.addToUi();
}
function importCourses() {
var optionalArgs = {
teacherId: 'me',
pageSize: 5
};
var response = Classroom.Courses.list();
var courses = response.courses;
for (var i = 0; i < 1; i++) {
var courseName = courses[i].name;
var courseId = courses[i].id;
insertCourse(courseName, courseId)
}
}
function insertCourse(courseName, courseId) {
var spreadsheetName = courseName + "(" + courseId + ")"
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var yourNewSheet = activeSpreadsheet.getSheetByName(spreadsheetName);
if (yourNewSheet != null) {
return
}
yourNewSheet = activeSpreadsheet.insertSheet();
yourNewSheet.setName(spreadsheetName);
yourNewSheet.appendRow(['Nombre', 'Email', 'Asistencia'])
yourNewSheet.setFrozenRows(1)
var studentNames = getRoster(courseId)["studentNames"]
var studentEmails = getRoster(courseId)["studentEmails"]
for (var i = 0; i < studentNames.length; i++) {
yourNewSheet.appendRow([studentNames[i],studentEmails[i]])
}
yourNewSheet.autoResizeColumns(1, 2)
yourNewSheet.setFrozenColumns(2)
}
function getRoster(courseId) {
var studentNames = []
var studentEmails = []
var optionalArgs = {
pageSize: 100
};
var response = Classroom.Courses.Students.list(courseId, optionalArgs)
var students = response.students
for (var i = 0; i < 100; i++) {
try {
studentNames.push(students[i].profile.name.fullName)
studentEmails.push(students[i].profile.emailAddress)
} catch (err) {
return { "studentNames":studentNames, "studentEmails":studentEmails }
}
}
}
function checkAll() {
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getDataRange().getValues();
for (var i = 2; i < sheet.length * 100; i++){
var meetCode = getCleanCode(sheet[0][i])
// No Meet code given
if (meetCode == null) {
break;
}
else {
// check whether each student was present in Meet
checkMeet(meetCode, i+1);
}
}
}
function checkMeet(meetCode, index) {
// universal settings - static
var userKey = 'all';
var applicationName = 'meet';
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getDataRange().getValues();
for (var i = 0; i < sheet.length-1; i++) {
var emailAddress = sheet[i+1][1]
var optionalArgs = {
event_name: "call_ended",
filters: "identifier==" + emailAddress + ",meeting_code==" + meetCode
};
try {
var response = AdminReports.Activities.list(userKey, applicationName, optionalArgs);
var activities = response.items;
if (activities == false) {
markAbsent(ss,i+2,index)
}
else {
markPresent(ss,i+2,index)
}
} catch (err) {
continue
}
}
}
function getCleanCode(meetCode) {
try{
return meetCode.replace("/-/g","")
} catch (err) { return meetCode; }
}
function markAbsent(sheet, i, j) {
var cell = sheet.getRange(i, j);
cell.setValue("Absent");
}
function markPresent(sheet, i, j) {
var cell = sheet.getRange(i, j);
cell.setValue("Present");
}
...
For the correct functionality of the code, check for if (activities == null) or if (activities == undefined) - NOT if (activities == false)
If there are no response items (that is no items have been found for a given user and meeting code) - activities will be undefined, but not false.
if (activities == false) will never be fulfilled and if you use it - all participants will be marked as present
On the other hand, if activities == undefined for other reasons - for example because you introduced the wrong meet code (don't forget to replace 'Asistencia' through a valid meet code), all participants will be marked as absent
Note that the Reports API has some delay, so do not expect to retrieve participation data in live time.
If all participants are marked as absent - probably the data did not propagate yet, wait some time and try again.
Make sure you pass the correct meet code to the script. Your meet code should look something like xxx-xxxx-xxx.
The script needs to remove the - for correct functionality. For this please change in function getCleanCode(meetCode) the line return meetCode.replace("/-/g","") to return meetCode.replace(/-/g, "");
As for the question in your comment: here you can see all available Hangouts Meet Audit Activity Events that you can use for filtering instead of the meeting_code. Unfortunately the timestamp is not one of possible query parameters.
Here is the code we have been using for a few months now, no glitches:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Asistencia')
.addItem('Asistencia', 'checkAll')
.addItem('*Crear*', 'createNewSheets')
.addToUi();
}
function createNewSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Get the range of cells that store employee data.
var employeeDataRange = ss.getRangeByName("A:A");
var employeeObjects = employeeDataRange.getValues();
var template = ss.getSheetByName('Template');
for (var i=0; i < employeeObjects.length; i++) {
// Put the sheet you want to create in a variable
var sheet = ss.getSheetByName(employeeObjects[i]);
// Check if the sheet you want to create already exists. If so,
// log this and loop back. If not, create the new sheet.
if (sheet) {
Logger.log("Sheet " + employeeObjects[i] + "already exists");
} else {
template.copyTo(ss).setName(employeeObjects[i]);
}
}
return;
}
function importCourses() {
var optionalArgs = {
teacherId: 'me',
pageSize: 5
};
var response = Classroom.Courses.list(optionalArgs);
var courses = response.courses;
for (var i = 0; i < 2; i++) {
var courseName = courses[i].name;
var courseId = courses[i].id;
insertCourse(courseName, courseId)
}
}
function insertCourse(courseName, courseId) {
var spreadsheetName = courseName + "(" + courseId + ")"
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var yourNewSheet = activeSpreadsheet.getSheetByName(spreadsheetName);
if (yourNewSheet != null) {
return
}
yourNewSheet = activeSpreadsheet.insertSheet();
yourNewSheet.setName(spreadsheetName);
yourNewSheet.appendRow(['Nombre', 'Email', 'Asistencia'])
yourNewSheet.setFrozenRows(1)
var studentNames = getRoster(courseId)["studentNames"]
var studentEmails = getRoster(courseId)["studentEmails"]
for (var i = 0; i < studentNames.length; i++) {
yourNewSheet.appendRow([studentNames[i],studentEmails[i]])
}
yourNewSheet.autoResizeColumns(1, 2)
yourNewSheet.setFrozenColumns(2)
}
function getRoster(courseId) {
var studentNames = []
var studentEmails = []
var optionalArgs = {
pageSize: 100
};
var response = Classroom.Courses.Students.list(courseId, optionalArgs)
var students = response.students
for (var i = 0; i < 100; i++) {
try {
studentNames.push(students[i].profile.name.fullName)
studentEmails.push(students[i].profile.emailAddress)
} catch (err) {
return { "studentNames":studentNames, "studentEmails":studentEmails }
}
}
}
function checkAll() {
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getDataRange().getValues();
for (var i = 2; i < sheet.length * 100; i++){
var meetCode = getCleanCode(sheet[0][i])
// No Meet code given
if (meetCode == null) {
break;
}
else {
// check whether each student was present in Meet
checkMeet(meetCode, i+1);
}
}
}
function checkMeet(meetCode, index) {
// universal settings - static
var userKey = 'all';
var applicationName = 'meet';
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getDataRange().getValues();
//let today = new Date()
//var staringTime = new Date().toISOString();
// var DateValue = ss.getRange(1, 1).getValues();
for (var i = 0; i < sheet.length-1; i++) {
var emailAddress = sheet[i+1][1]
var optionalArgs = {
startTime: formatStartDate(),
event_name: "call_ended",
filters: "identifier==" + emailAddress + ",meeting_code==" + meetCode
};
try {
var response = AdminReports.Activities.list(userKey, applicationName, optionalArgs);
var activities = response.items;
if (activities == undefined) {
markAbsent(ss,i+2,index)
}
else {
markPresent(ss,i+2,index)
}
} catch (err) {
continue
}
}
}
function getCleanCode(meetCode) {
try{
return meetCode.replace(/-/g, "");
} catch (err) { return meetCode; }
}
function markAbsent(sheet, i, j) {
var cell = sheet.getRange(i, j);
cell.setValue("A");
}
function markPresent(sheet, i, j) {
var cell = sheet.getRange(i, j);
cell.setValue("");
}
function formatStartDate(){
let date = new Date();
const offset = date.getTimezoneOffset();
let offsetDate = new Date(date.getTime() - (offset*145*1000));
return offsetDate.toISOString();
}

New answer to old question about Google Sheets/Calendar

I found this old question about synchronizing Google Sheets and Google Calendar and tried to download the template and then edit it accordingly.
When I try to run the script it prompts this error ReferenceError: calenderId is not defined.
Since I can only see one line, 9, where to to enter the calendarId I can not make the script work.
Did I miss a line in my example? I am aware that I need to write my cal-id in line 9.
// Script to synchronize a calendar to a spreadsheet and vice versa.
//
// See https://github.com/Davepar/gcalendarsync for instructions on setting this up.
//
// Set this value to match your calendar!!!
// Calendar ID can be found in the "Calendar Address" section of the Calendar Settings.
//var calendarId = '<your-calendar-id>#group.calendar.google.com';
var calendarId = 'Her er min kalender-adresse, naturligvis';
// Set the beginning and end dates that should be synced. beginDate can be set to Date() to use
// today. The numbers are year, month, date, where month is 0 for Jan through 11 for Dec.
var beginDate = new Date(1970, 0, 1); // Default to Jan 1, 1970
var endDate = new Date(2500, 0, 1); // Default to Jan 1, 2500
// Date format to use in the spreadsheet.
var dateFormat = 'M/d/yyyy H:mm';
var titleRowMap = {
'title': 'Title',
'description': 'Description',
'location': 'Location',
'starttime': 'Start Time',
'endtime': 'End Time',
'guests': 'Guests',
'color': 'Color',
'id': 'Id'
};
var titleRowKeys = ['title', 'description', 'location', 'starttime', 'endtime', 'guests', 'color', 'id'];
var requiredFields = ['id', 'title', 'starttime', 'endtime'];
// This controls whether email invites are sent to guests when the event is created in the
// calendar. Note that any changes to the event will cause email invites to be resent.
var SEND_EMAIL_INVITES = false;
// Setting this to true will silently skip rows that have a blank start and end time
// instead of popping up an error dialog.
var SKIP_BLANK_ROWS = false;
// Updating too many events in a short time period triggers an error. These values
// were successfully used for deleting and adding 240 events. Values in milliseconds.
var THROTTLE_SLEEP_TIME = 200;
var MAX_RUN_TIME = 5.75 * 60 * 1000;
// Special flag value. Don't change.
var EVENT_DIFFS_WITH_GUESTS = 999;
// Adds the custom menu to the active spreadsheet.
function onOpen() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [
{
name: "Update from Calendar",
functionName: "syncFromCalendar"
}, {
name: "Update to Calendar",
functionName: "syncToCalendar"
}
];
spreadsheet.addMenu('Calendar Sync', menuEntries);
}
// Creates a mapping array between spreadsheet column and event field name
function createIdxMap(row) {
var idxMap = [];
for (var idx = 0; idx < row.length; idx++) {
var fieldFromHdr = row[idx];
for (var 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 reformatEvent(row, idxMap, keysToAdd) {
var reformatted = row.reduce(function(event, value, idx) {
if (idxMap[idx] != null) {
event[idxMap[idx]] = value;
}
return event;
}, {});
for (var k in keysToAdd) {
reformatted[keysToAdd[k]] = '';
}
return reformatted;
}
// Converts a calendar event to a psuedo-sheet event.
function convertCalEvent(calEvent) {
convertedEvent = {
'id': calEvent.getId(),
'title': calEvent.getTitle(),
'description': calEvent.getDescription(),
'location': calEvent.getLocation(),
'guests': calEvent.getGuestList().map(function(x) {return x.getEmail();}).join(','),
'color': calEvent.getColor()
};
if (calEvent.isAllDayEvent()) {
convertedEvent.starttime = calEvent.getAllDayStartDate();
var endtime = calEvent.getAllDayEndDate();
if (endtime - convertedEvent.starttime === 24 * 3600 * 1000) {
convertedEvent.endtime = '';
} else {
convertedEvent.endtime = endtime;
if (endtime.getHours() === 0 && endtime.getMinutes() == 0) {
convertedEvent.endtime.setSeconds(endtime.getSeconds() - 1);
}
}
} else {
convertedEvent.starttime = calEvent.getStartTime();
convertedEvent.endtime = calEvent.getEndTime();
}
return convertedEvent;
}
// Converts calendar event into spreadsheet data row
function calEventToSheet(calEvent, idxMap, dataRow) {
convertedEvent = convertCalEvent(calEvent);
for (var idx = 0; idx < idxMap.length; idx++) {
if (idxMap[idx] !== null) {
dataRow[idx] = convertedEvent[idxMap[idx]];
}
}
}
// Returns empty string or time in milliseconds for Date object
function getEndTime(ev) {
return ev.endtime === '' ? '' : ev.endtime.getTime();
}
// Determines the number of field differences between a calendar event and
// a spreadsheet event
function eventDifferences(convertedCalEvent, sev) {
var eventDiffs = 0 + (convertedCalEvent.title !== sev.title) +
(convertedCalEvent.description !== sev.description) +
(convertedCalEvent.location !== sev.location) +
(convertedCalEvent.starttime.toString() !== sev.starttime.toString()) +
(getEndTime(convertedCalEvent) !== getEndTime(sev)) +
(convertedCalEvent.guests !== sev.guests) +
(convertedCalEvent.color !== ('' + sev.color));
if (eventDiffs > 0 && convertedCalEvent.guests) {
// Use a special flag value if an event changed, but it has guests.
eventDiffs = EVENT_DIFFS_WITH_GUESTS;
}
return eventDiffs;
}
// Determine whether required fields are missing
function areRequiredFieldsMissing(idxMap) {
return requiredFields.some(function(val) {
return idxMap.indexOf(val) < 0;
});
}
// Returns list of fields that aren't in spreadsheet
function missingFields(idxMap) {
return titleRowKeys.filter(function(val) {
return idxMap.indexOf(val) < 0;
});
}
// Set up formats and hide ID column for empty spreadsheet
function setUpSheet(sheet, fieldKeys) {
sheet.getRange(1, fieldKeys.indexOf('starttime') + 1, 999).setNumberFormat(dateFormat);
sheet.getRange(1, fieldKeys.indexOf('endtime') + 1, 999).setNumberFormat(dateFormat);
sheet.hideColumns(fieldKeys.indexOf('id') + 1);
}
// Display error alert
function errorAlert(msg, evt, ridx) {
var ui = SpreadsheetApp.getUi();
if (evt) {
ui.alert('Skipping row: ' + msg + ' in event "' + evt.title + '", row ' + (ridx + 1));
} else {
ui.alert(msg);
}
}
// Updates a calendar event from a sheet event.
function updateEvent(calEvent, convertedCalEvent, sheetEvent){
var numChanges = 0;
sheetEvent.sendInvites = SEND_EMAIL_INVITES;
if (convertedCalEvent.starttime.toString() !== sheetEvent.starttime.toString() ||
getEndTime(convertedCalEvent) !== getEndTime(sheetEvent)) {
if (sheetEvent.endtime === '') {
calEvent.setAllDayDate(sheetEvent.starttime);
} else {
calEvent.setTime(sheetEvent.starttime, sheetEvent.endtime);
}
numChanges++;
}
if (convertedCalEvent.title !== sheetEvent.title) {
calEvent.setTitle(sheetEvent.title);
numChanges++;
}
if (convertedCalEvent.description !== sheetEvent.description) {
calEvent.setDescription(sheetEvent.description);
numChanges++;
}
if (convertedCalEvent.location !== sheetEvent.location) {
calEvent.setLocation(sheetEvent.location);
numChanges++;
}
if (convertedCalEvent.color !== ('' + sheetEvent.color)) {
if (sheetEvent.color > 0 && sheetEvent.color < 12) {
calEvent.setColor('' + sheetEvent.color);
numChanges++;
}
}
if (convertedCalEvent.guests !== sheetEvent.guests) {
var guestCal = calEvent.getGuestList().map(function (x) {
return {
email: x.getEmail(),
added: false
};
});
var sheetGuests = sheetEvent.guests || '';
var guests = sheetGuests.split(',').map(function (x) {
return x ? x.trim() : '';
});
// Check guests that are already invited.
for (var gIx = 0; gIx < guestCal.length; gIx++) {
var index = guests.indexOf(guestCal[gIx].email);
if (index >= 0) {
guestCal[gIx].added = true;
guests.splice(index, 1);
}
}
guests.forEach(function (guest) {
if (guest) {
calEvent.addGuest(guest);
numChanges++;
}
});
guestCal.forEach(function (guest) {
if (!guest.added) {
calEvent.removeGuest(guest.email);
numChanges++;
}
});
}
// Throttle updates.
Utilities.sleep(THROTTLE_SLEEP_TIME * numChanges);
return numChanges;
}
// Synchronize from calendar to spreadsheet.
function syncFromCalendar() {
console.info('Starting sync from calendar');
// Get calendar and events
var calendar = CalendarApp.getCalendarById(calendarId);
var calEvents = calendar.getEvents(beginDate, endDate);
// Get spreadsheet and data
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
var range = sheet.getDataRange();
var data = range.getValues();
var eventFound = new Array(data.length);
// Check if spreadsheet is empty and add a title row
var titleRow = [];
for (var idx = 0; idx < titleRowKeys.length; idx++) {
titleRow.push(titleRowMap[titleRowKeys[idx]]);
}
if (data.length < 1) {
data.push(titleRow);
range = sheet.getRange(1, 1, data.length, data[0].length);
range.setValues(data);
setUpSheet(sheet, titleRowKeys);
}
if (data.length == 1 && data[0].length == 1 && data[0][0] === '') {
data[0] = titleRow;
range = sheet.getRange(1, 1, data.length, data[0].length);
range.setValues(data);
setUpSheet(sheet, titleRowKeys);
}
// Map spreadsheet headers to indices
var idxMap = createIdxMap(data[0]);
var idIdx = idxMap.indexOf('id');
// Verify header has all required fields
if (areRequiredFieldsMissing(idxMap)) {
var reqFieldNames = requiredFields.map(function(x) {return titleRowMap[x];}).join(', ');
errorAlert('Spreadsheet must have ' + reqFieldNames + ' columns');
return;
}
// Array of IDs in the spreadsheet
var sheetEventIds = data.slice(1).map(function(row) {return row[idIdx];});
// Loop through calendar events
for (var cidx = 0; cidx < calEvents.length; cidx++) {
var calEvent = calEvents[cidx];
var calEventId = calEvent.getId();
var ridx = sheetEventIds.indexOf(calEventId) + 1;
if (ridx < 1) {
// Event not found, create it
ridx = data.length;
var newRow = [];
var rowSize = idxMap.length;
while (rowSize--) newRow.push('');
data.push(newRow);
} else {
eventFound[ridx] = true;
}
// Update event in spreadsheet data
calEventToSheet(calEvent, idxMap, data[ridx]);
}
// Remove any data rows not found in the calendar
var rowsDeleted = 0;
for (var idx = eventFound.length - 1; idx > 0; idx--) {
//event doesn't exists and has an event id
if (!eventFound[idx] && sheetEventIds[idx - 1]) {
data.splice(idx, 1);
rowsDeleted++;
}
}
// Save spreadsheet changes
range = sheet.getRange(1, 1, data.length, data[0].length);
range.setValues(data);
if (rowsDeleted > 0) {
sheet.deleteRows(data.length + 1, rowsDeleted);
}
}
// Synchronize from spreadsheet to calendar.
function syncToCalendar() {
console.info('Starting sync to calendar');
var scriptStart = Date.now();
// Get calendar and events
var calendar = CalendarApp.getCalendarById(calendarId);
if (!calendar) {
errorAlert('Cannot find calendar. Check instructions for set up.');
}
var calEvents = calendar.getEvents(beginDate, endDate);
var calEventIds = calEvents.map(function(val) {return val.getId();});
// Get spreadsheet and data
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
var range = sheet.getDataRange();
var data = range.getValues();
if (data.length < 2) {
errorAlert('Spreadsheet must have a title row and at least one data row');
return;
}
// Map headers to indices
var idxMap = createIdxMap(data[0]);
var idIdx = idxMap.indexOf('id');
var idRange = range.offset(0, idIdx, data.length, 1);
var idData = idRange.getValues()
// Verify header has all required fields
if (areRequiredFieldsMissing(idxMap)) {
var reqFieldNames = requiredFields.map(function(x) {return titleRowMap[x];}).join(', ');
errorAlert('Spreadsheet must have ' + reqFieldNames + ' columns');
return;
}
var keysToAdd = missingFields(idxMap);
// Loop through spreadsheet rows
var numAdded = 0;
var numUpdates = 0;
var eventsAdded = false;
for (var ridx = 1; ridx < data.length; ridx++) {
var sheetEvent = reformatEvent(data[ridx], idxMap, keysToAdd);
// If enabled, skip rows with blank/invalid start and end times
if (SKIP_BLANK_ROWS && !(sheetEvent.starttime instanceof Date) &&
!(sheetEvent.endtime instanceof Date)) {
continue;
}
// Do some error checking first
if (!sheetEvent.title) {
errorAlert('must have title', sheetEvent, ridx);
continue;
}
if (!(sheetEvent.starttime instanceof Date)) {
errorAlert('start time must be a date/time', sheetEvent, ridx);
continue;
}
if (sheetEvent.endtime !== '') {
if (!(sheetEvent.endtime instanceof Date)) {
errorAlert('end time must be empty or a date/time', sheetEvent, ridx);
continue;
}
if (sheetEvent.endtime < sheetEvent.starttime) {
errorAlert('end time must be after start time for event', sheetEvent, ridx);
continue;
}
}
// Ignore events outside of the begin/end range desired.
if (sheetEvent.starttime > endDate) {
continue;
}
if (sheetEvent.endtime === '') {
if (sheetEvent.starttime < beginDate) {
continue;
}
} else {
if (sheetEvent.endtime < beginDate) {
continue;
}
}
// Determine if spreadsheet event is already in calendar and matches
var addEvent = true;
if (sheetEvent.id) {
var eventIdx = calEventIds.indexOf(sheetEvent.id);
if (eventIdx >= 0) {
calEventIds[eventIdx] = null; // Prevents removing event below
addEvent = false;
var calEvent = calEvents[eventIdx];
var convertedCalEvent = convertCalEvent(calEvent);
var eventDiffs = eventDifferences(convertedCalEvent, sheetEvent);
if (eventDiffs > 0) {
// When there are only 1 or 2 event differences, it's quicker to
// update the event. For more event diffs, delete and re-add the event. The one
// exception is if the event has guests (eventDiffs=99). We don't
// want to force guests to re-confirm, so go through the slow update
// process instead.
if (eventDiffs < 3 && eventDiffs !== EVENT_DIFFS_WITH_GUESTS) {
numUpdates += updateEvent(calEvent, convertedCalEvent, sheetEvent);
} else {
addEvent = true;
calEventIds[eventIdx] = sheetEvent.id;
}
}
}
}
console.info('%d updates, time: %d msecs', numUpdates, Date.now() - scriptStart);
if (addEvent) {
var newEvent;
sheetEvent.sendInvites = SEND_EMAIL_INVITES;
if (sheetEvent.endtime === '') {
newEvent = calendar.createAllDayEvent(sheetEvent.title, sheetEvent.starttime, sheetEvent);
} else {
newEvent = calendar.createEvent(sheetEvent.title, sheetEvent.starttime, sheetEvent.endtime, sheetEvent);
}
// Put event ID back into spreadsheet
idData[ridx][0] = newEvent.getId();
eventsAdded = true;
// Set event color
if (sheetEvent.color > 0 && sheetEvent.color < 12) {
newEvent.setColor('' + sheetEvent.color);
}
// Throttle updates.
numAdded++;
Utilities.sleep(THROTTLE_SLEEP_TIME);
if (numAdded % 10 === 0) {
console.info('%d events added, time: %d msecs', numAdded, Date.now() - scriptStart);
}
}
// If the script is getting close to timing out, save the event IDs added so far to avoid lots
// of duplicate events.
if ((Date.now() - scriptStart) > MAX_RUN_TIME) {
idRange.setValues(idData);
}
}
// Save spreadsheet changes
if (eventsAdded) {
idRange.setValues(idData);
}
// Remove any calendar events not found in the spreadsheet
var numToRemove = calEventIds.reduce(function(prevVal, curVal) {
if (curVal !== null) {
prevVal++;
}
return prevVal;
}, 0);
if (numToRemove > 0) {
var ui = SpreadsheetApp.getUi();
var response = ui.alert('Delete ' + numToRemove + ' calendar event(s) not found in spreadsheet?',
ui.ButtonSet.YES_NO);
if (response == ui.Button.YES) {
var numRemoved = 0;
calEventIds.forEach(function(id, idx) {
if (id != null) {
calEvents[idx].deleteEvent();
Utilities.sleep(THROTTLE_SLEEP_TIME);
numRemoved++;
if (numRemoved % 10 === 0) {
console.info('%d events removed, time: %d msecs', numRemoved, Date.now() - scriptStart);
}
}
});
}
}
}
// Set up a trigger to automatically update the calendar when the spreadsheet is
// modified. See the instructions for how to use this.
function createSpreadsheetEditTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('syncToCalendar')
.forSpreadsheet(ss)
.onEdit()
.create();
}
// Delete the trigger. Use this to stop automatically updating the calendar.
function deleteTrigger() {
// Loop over all triggers.
var allTriggers = ScriptApp.getProjectTriggers();
for (var idx = 0; idx < allTriggers.length; idx++) {
if (allTriggers[idx].getHandlerFunction() === 'syncToCalendar') {
ScriptApp.deleteTrigger(allTriggers[idx]);
}
}
}
The ReferenceError: calenderId is not defined error message you are getting is due to the fact that you are declaring calenderId instead of calendarId and you are using calendarId all over your code.
So in order to fix your error you might want to check the line at which you are declaring the variable and declare it with the appropriate name.

Using Directory API in Google-Apps-Script, how to get groups inside a group

I'm working on a script that gets all goups in my domain and lists all users in these groups.
I rewrote this script because GroupsManager is deprecated so my old script will stop working soon.
All is working well (although this API is quite slow...) but the issue I have is that some groups have not only users but include also other groups... some of them have only subgroups (and no users (way of speaking !)
I can't find any way to catch that situation, the list method return users, it ignores completely groups inside.
So my question is :
Is there a way to get the groups inside a group ? Am I missing something obvious ?
Below and for info is the code I use to list all members in groups in a spreadsheet, 1 sheet / group so that I can manipulate these data faster and easier later on. I used background color flags as a track keeper to handle a (relatively) small batch operation and a timer trigger that make it work until it's done. (yes, we have a lot of groups in our domain ;-)
function listGroupMembers() {
var start = new Date().getTime();
var ss = SpreadsheetApp.openById('1k-o4IVKEhW2zkk_________f2rH4UUt3OAC8I0ZoM');// set your SS ID (runs with timer trigger > needs to open by ID
var pageToken, page;
var count=0;
var groupList = [];
do {
page = AdminDirectory.Groups.list({
domain: 'xxxxx.be',
maxResults: 100,
pageToken: pageToken
});
var gr = page.groups;
if (gr) {
for (var i = 0; i < gr.length; i++) {
var group = gr[i];
count++;
//Logger.log(group);
groupList.push([group.email,count])
}
} else {
Logger.log('No group found.');
}
pageToken = page.nextPageToken;
}
while (pageToken);
try{
var GroupAddress = ss.getSheetByName('GroupAddress');// if GroupAddress Sheet already exist, open it
Logger.log('Use existing sheet "'+GroupAddress.getName()+'"');
}catch(err){
Logger.log('create sheet "GroupAddress"');
ss.deleteSheet(ss.getSheets()[0]);
var GroupAddress = ss.insertSheet('GroupAddress',0); //else create it as first sheet
}
GroupAddress.getDataRange().clearContent();
ss.getSheetByName('GroupAddress').getRange(1,1,groupList.length,groupList[0].length).setValues(groupList);
var groupNames = ss.getSheetByName("GroupAddress").getDataRange().getValues();
var groupDone = ss.getSheetByName("GroupAddress").getDataRange().getBackgrounds();
var progress;
for(var n in groupNames){
if(groupDone[n][0] != 'white' || (new Date().getTime()-start)/60 > 5000){ // limit to 5 seconds + 1 group
continue;
}
try{
var outputSheet = ss.insertSheet(groupNames[n][0],Number(n)+1)
}catch(err){
var outputSheet = ss.getSheetByName(groupNames[n][0]);
}
var group = GroupsApp.getGroupByEmail(groupNames[n][0]);
//Logger.log('groupNames[n][0] = '+groupNames[n][0]);
try{
var users = group.getUsers();
var output = [];
for (var i in users){
var user = AdminDirectory.Users.get(users[i].getEmail());
output.push([Number(i)+1,user.name.fullName, user.primaryEmail,user.aliases!=null?user.aliases:'',new Date(user.creationTime),user.isAdmin,new Date(user.lastLoginTime)]);
}
ss.getSheetByName("GroupAddress").getRange(Number(n)+1,1).setBackground('#fff2cc');
SpreadsheetApp.flush();
if(output.length>0){
output.push(['durée :',parseInt((new Date().getTime()-start)/60),'millisecondes','','','','']);
outputSheet.getRange(1,1,output.length,output[0].length).setValues(output);
}
}catch(err){
continue}
}
groupDone = ss.getSheetByName("GroupAddress").getDataRange().getBackgrounds();
for(progress = groupDone.length-1;progress>=0;progress--){
if(groupDone[progress][0]!='white'){break};
}
Logger.log(n+'='+progress+'?');
if(n==progress){
MailApp.sendEmail(Session.getEffectiveUser().getEmail(),'All jobs done in GroupList','All tasks completed on '+new Date().toLocaleString());
var trig = ScriptApp.getProjectTriggers();
for(var t in trig){
try{
ScriptApp.deleteTrigger(trig[t]);
}catch(e){}
}
}else{
ScriptApp.newTrigger('listGroupMembers').timeBased().after(3000).create();// wait 3 secs and continue
}
}
EDIT
Thanks to Göran's answer I have a version that gets all members of all groups including subgroups.
I changed (very) slightly his code to fit my requirements and I also needed to limit the number of cells in each sheet to what I needed because the total (1000*26 cells/sheet) was exceeding the 200000 cells limit (I have more than 100 sheets).
Full code below for anybody who would be interested (including menu and a few utilities):
function onOpen() {
var menu = SpreadsheetApp.getUi().createMenu("Utilitaires INSAS")
.addItem("reset colors", "resetColors")
.addItem("create groupUser lists", "listGroupMembers")
.addItem("delete all sheets", "delsheets");
menu.addToUi();
}
function listGroupMembers() {
var start = new Date().getTime();
var ss = SpreadsheetApp.openById('1k-o4IVKEhW2zkkEu0gAvb92rQf2rH4UUt3OAC8I0ZoM');// set your SS ID (runs with timer trigger > needs to open by ID
var pageToken, page;
var count=0;
var groupList = [];
do {
page = AdminDirectory.Groups.list({
domain: 'insas.be',
maxResults: 100,
pageToken: pageToken
});
var gr = page.groups;
if (gr) {
for (var i = 0; i < gr.length; i++) {
var group = gr[i];
count++;
//Logger.log(group);
groupList.push([group.email,count])
}
} else {
Logger.log('No group found.');
}
pageToken = page.nextPageToken;
}
while (pageToken);
try{
var GroupAddress = ss.getSheetByName('GroupAddress');// if GroupAddress Sheet already exist, open it
Logger.log('Use existing sheet "'+GroupAddress.getName()+'"');
}catch(err){
Logger.log('create sheet "GroupAddress"');
ss.deleteSheet(ss.getSheets()[0]);
var GroupAddress = ss.insertSheet('GroupAddress',0); //else create it as first sheet
}
GroupAddress.getDataRange().clearContent();
ss.getSheetByName('GroupAddress').getRange(1,1,groupList.length,groupList[0].length).setValues(groupList);
var groupNames = ss.getSheetByName("GroupAddress").getDataRange().getValues();
var groupDone = ss.getSheetByName("GroupAddress").getDataRange().getBackgrounds();
var progress;
for(var n in groupNames){
if(groupDone[n][0] != 'white' || (new Date().getTime()-start)/60 > 5000){ // limit to 5 seconds + 1 group
continue;
}
Logger.log('groupNames[n][0] = '+groupNames[n][0]);
try{
var outputSheet = ss.getSheetByName(groupNames[n][0]);
Logger.log('sheet '+outputSheet.getName()+' created');
}catch(err){
var outputSheet = ss.insertSheet(groupNames[n][0],ss.getSheets().length+1);
}
try{
var users = getUsersInGroup(groupNames[n][0]);
var output = [];
for (var i in users){
var user = users[i];
// Logger.log(user);
output.push([Number(i)+1,user.email,user.role,user.groupName]);
}
ss.getSheetByName("GroupAddress").getRange(Number(n)+1,1).setBackground('#fff2cc');
SpreadsheetApp.flush();
if(output.length>0){
output.push(['Ex.T = '+parseInt((new Date().getTime()-start)/60)+' mS','','','']);
outputSheet.getRange(1,1,output.length,output[0].length).setValues(output);
outputSheet.deleteRows(output.length+2, outputSheet.getMaxRows()-output.length-4);
outputSheet.deleteColumns(6, outputSheet.getMaxColumns()-6);
}
}catch(err){
continue}
}
groupDone = ss.getSheetByName("GroupAddress").getDataRange().getBackgrounds();
for(progress = groupDone.length-1;progress>=0;progress--){
if(groupDone[progress][0]!='white'){break};
}
Logger.log(n+'='+progress+'?');
if(n==progress){
MailApp.sendEmail(Session.getEffectiveUser().getEmail(),'All jobs done in GroupList','All tasks completed on '+new Date().toLocaleString());
var trig = ScriptApp.getProjectTriggers();
for(var t in trig){
try{
ScriptApp.deleteTrigger(trig[t]);
}catch(e){}
}
}else{
ScriptApp.newTrigger('listGroupMembers').timeBased().after(10000).create();// wait 3 secs and continue
}
}
function resetColors(){
var ss = SpreadsheetApp.getActive();
ss.getSheets()[0].getDataRange().setBackground(null);
}
function getUsersInGroup(rootGroup) {
var groupTreeUsers = [];
var groups = [];
groups.push(rootGroup);
while (groups.length > 0) {
var currentGroup = groups.pop();
var groupName = AdminDirectory.Groups.get(currentGroup).name;
var groupMembers = getAllMembers_(currentGroup);
for (var i in groupMembers) {
if (groupMembers[i].type == 'USER') {
var groupMember = groupMembers[i];
groupMember['groupName'] = currentGroup;
groupTreeUsers.push(groupMember)
}
else if (groupMembers[i].type == 'GROUP') {
groups.push(groupMembers[i].email)
}
}
}
Logger.log('There are %s user members in %s (including sub groups)', groupTreeUsers.length, rootGroup);
return groupTreeUsers;
}
function getAllMembers_(group) {
var memberPageToken, memberPage;
var members = [];
do {
memberPage = AdminDirectory.Members.list(group, {
maxResults: 200,
pageToken: memberPageToken
});
var pageMembers = memberPage.members;
if (pageMembers) {
for (var j =0; j < pageMembers.length; j++) {
members.push(pageMembers[j]);
}
}
memberPageToken = memberPage.nextPageToken;
} while (memberPageToken);
return members;
}
function delsheets(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var numSheets = ss.getNumSheets();// check how many sheets in the spreadsheet
for (var pa=numSheets-1;pa>0;pa--){
if(ss.getSheets()[pa].getSheetName()!='GroupAddress'){
ss.deleteSheet(ss.getSheets()[pa]); // delete sheets begining with the last one
Utilities.sleep(100);
}
}
SpreadsheetApp.flush();
}
AdminDirectory MembersCollection might be what you are looking for.
Example snippet:
var members = AdminDirectory.Members.list('testgroup#example.com').members;
for (var i = 0; i < members.length; i++) {
var member = members[i];
switch (member.type) {
case 'USER':
Logger.log('%s is a user', member.email);
break;
case 'GROUP':
Logger.log('%s is a group', member.email);
break;
default:
Logger.log('This will never happen');
}
Some of my customers have groups containing groups contaning groups... forming a group tree with arbitrary depth. I use this code to get all user members in such a group tree:
function walkTreeStack() {
var rootGroup = 'testgroup#example.com';
var groupTreeUsers = [];
var groups = [];
groups.push(rootGroup);
while (groups.length > 0) {
var currentGroup = groups.pop();
var groupName = AdminDirectory.Groups.get(currentGroup).name;
var groupMembers = getAllMembers_(currentGroup);
for (var i in groupMembers) {
if (groupMembers[i].type == 'USER') {
groupTreeUsers.push([groupName, groupMembers[i].email])
}
else if (groupMembers[i].type == 'GROUP') {
groups.push(groupMembers[i].email)
}
}
}
Logger.log('There are %s user members in %s (including sub groups)', groupTreeUsers.length, groupName);
}
function getAllMembers_(group) {
var memberPageToken, memberPage;
var members = [];
do {
memberPage = AdminDirectory.Members.list(group, {
maxResults: 200,
pageToken: memberPageToken
});
var pageMembers = memberPage.members;
if (pageMembers) {
for (var j =0; j < pageMembers.length; j++) {
members.push(pageMembers[j]);
}
}
memberPageToken = memberPage.nextPageToken;
} while (memberPageToken);
return members;
}
It's slightly faster if it's done recursively but I suspect the script might crash due to memory constraints if there are a large number of sub groups and user members:
function getGroupTreeMembers() {
var rootGroup = 'testgroup#example.com';
var groupTreeUsers = [];
walkTreeRecursive_(rootGroup, groupTreeUsers);
var groupName = AdminDirectory.Groups.get(rootGroup).name;
Logger.log('There are %s user members in %s (including sub groups)', groupTreeUsers.length, groupName);
}
function walkTreeRecursive_(rootGroup, groupTreeUsers) {
var groupName = AdminDirectory.Groups.get(rootGroup).name;
var groupMembers = getAllMembers_(rootGroup);
var groups = [];
for (var i in groupMembers) {
if (groupMembers[i].type == 'USER') {
groupTreeUsers.push([groupName, groupMembers[i].email])
}
else if (groupMembers[i].type == 'GROUP') {
groups.push(groupMembers[i].email)
}
}
for (var i in groups) {
walkTreeRecursive_(groups[i], groupTreeUsers);
}
}
For more information please visit:
https://developers.google.com/admin-sdk/directory/v1/reference/members#resource