function is not getting the value from parent function - google apps script - google-apps-script

i have table, the headers are the project number, the left column is the workers name, i am adding code to start and stop working for each workers in the project.
so, when user edit the suitable rectangle it will add new (in or Out) record about that worker and the project.
I have problem with the function called area() , it is not getting the range value from the function onEdit(e).
it is working only when i put the value inside the area() function itself., for example when i put
var vrange = "S10"
or i run it with the fixed value such area("S10")
it is not getting the range variable value when the other functions are getting it correctly
but the range value is dynamic as per the user editing cell.
the problem is happening in this row for only area(range) function, the other functions such getProject(range) is getting the range value correctly.
addRecord(getProject(range),getWorker(range),new Date(),"un",area(range))
hope somebody can know what is my mistake.
here is the code
function onEdit(e) {
var range = e.range
Logger.log("e.range" + range)
var Currentsheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName()
if (Currentsheet == TimeInterface()) {
addRecord(getProject(range), getWorker(range), new Date(), "un", area(range))
}
}
function area(vrange) {
// var frange = "S10"
//var frange = vrange
Logger.log("vrange is " + vrange)
var currentrow = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(vrange).getRow()
var currentcol = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(vrange).getColumn()
var outColumn = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(RecordSettingsSheet()).getRange("B1").getValue()
var recordingFirstColumn = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(RecordSettingsSheet()).getRange("B2").getValue()
var recordingLastColumn = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(RecordSettingsSheet()).getRange("B3").getValue()
var recordingFirstRow = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(RecordSettingsSheet()).getRange("B4").getValue()
var recordingLastRow = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(RecordSettingsSheet()).getRange("B5").getValue()
var answer = ""
if (currentcol == outColumn && currentrow >= recordingFirstRow && currentrow <= recordingLastRow) {
answer = "out"
return answer
} else if (currentrow >= recordingFirstColumn && currentrow <= recordingLastColumn && currentrow >= recordingFirstRow && currentrow <= recordingLastRow) {
answer = "in"
return answer
} else {
answer = "Error"
return answer
}
}
function TimeInterface() {
var TimeInterfacev = "Interface"
return TimeInterfacev
}
function RecordSettingsSheet() {
var recordSettingsSheet = "Record Settings"
return recordSettingsSheet
}
function getProject(range) {
var ProjectID = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(1, range.getColumn()).getValues()
Logger.log("getProject range is " + range)
return ProjectID
}
function getWorker(range) {
var WorkerID = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(range.getRow(), 1).getValues()
return WorkerID
}
function setValue(cellname, Value) {
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(Sheetname()).getRange(cellname).setValue(Value);
}
function getValue(cellname) {
return SpreadsheetApp.getActiveSpreadsheet().getSheetByName(Sheetname()).getRange(cellname).getValue();
}
function Sheetname() {
var Sheet = "TimeRecords"
return Sheet
}
function newRowNumber() {
var row = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(Sheetname()).getLastRow() + 1;
return row
}
function addRecord(a, b, c, d, e) {
var row = newRowNumber();
var aa = getValue("A" + (row - 1))
var bb = getValue("B" + (row - 1))
var cc = getValue("C" + (row - 1))
var dd = getValue("D" + (row - 1))
if (
a == aa && b == bb && d == dd
) {
} else {
setValue("A" + row, a);
setValue("B" + row, b);
setValue("C" + row, c);
setValue("D" + row, d);
setValue("E" + row, e);
}
}
here is the error detailes
{
insertId: "ofc7bcfvzisc2"
jsonPayload: {
context: {
reportLocation: {
filePath: "Code"
functionName: "area"
lineNumber: 22
}
}
message: "Range not found
at area(Code:22)
at onEdit(Code:12)
"
serviceContext: {
service: "AKfycbyZxuwklLCadxRO93yHKQH0HO34bM9fReTelwt2I39_"
}
}
labels: {
script.googleapis.com/process_id: "EAEA1GOx_xjeztWsGim9PR3gYwydfe6FUt9_U2QNlaA6mIDJNtAm21jdbcWIBqG3z1Hg9CPQuaS_Hf5tJYJh8XqtqDQvgwfh1AX6DQ1M5Jc5tfPEOrgRj7eM4-ueK9m5FidEZBs31l3e-MsG9aHj4JLFEcsntzvvPMZB20Q"
script.googleapis.com/project_key: "MvVaniAhXvNj_E5qpMKyh9JCBc1Z0ol9u"
script.googleapis.com/user_key: "AJw2Yz+oU8zemc1ioio5IJNfc43E3QhuOpzprTTJO/RULzh/3VQSMjron9r4W4J+nuGW9Hj2YCF1"
}
logName: "projects/project-id-5448248979683187508/logs/script.googleapis.com%2Fconsole_logs"
receiveTimestamp: "2018-08-11T15:29:53.172475577Z"
resource: {
labels: {
function_name: "onEdit"
invocation_type: "event"
project_id: "project-id-5448248979683187508"
}
type: "app_script_function"
}
severity: "ERROR"
timestamp: "2018-08-11T15:29:52.164Z"
}

I solved the problem with using
getRange(range.getRow(), range.getColumn()).getRow()
instead of
getRange(range).getRow();

Related

Checking two columns and grouping to validate entries

I have data sheet as below-
I am trying to check for values in column Count of deletions if it is empty or not a number to flag an error at an account level,
because there will be only one entry of Count of deletions which will be at the first row entry of Account ID (unique) as illustrated in the pic above.
I did this to just validate the column Count of deletions
function myFunction() {
SS = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SS.getSheetByName("Sheet1");
var Validation_sheet = SS.getSheetByName("Sheet2");
var last_row = sheet.getLastRow();
var values = sheet.getRange(2,5,last_row).getValues();
var Status_values = sheet.getRange(2,4,last_row).getValues();
var result = isnumberorEmpty(values, Status_values);
const Count_setvalue = Validation_sheet.getRange("A2");
var errors_c = [];
for (key in result){
var check1 = result[key] == "empty";
var check2 = result[key] == "Not a number";
if(check1 == true || check2 == true){
errors_c.push(key);
}
}
if (errors_c.length > 0){
Count_setvalue.setValue("Row(s) " + errors_c.join(" and ") + " have error");
}
else{
Count_setvalue.setValue("No errors");
}
}
function isnumberorEmpty(array, array1) {
let result = {};
for (let i = 0; i < array.length-1; i++) {
if( array1[i] == "Completed"){
let row = array[i];
if (row[0] === "") {
result[i+2] = "empty";
}
else if (row[0] === "Ignored") {
result[i+2] = "No error";
}
else if (!isNaN(row[0])) {
result[i+2] = "number";
}
else {
result[i+2] = "Not a number";
}
}
else{
}
}
return result;
}
The above checks for column Count of deletions and validates only where corresponding rows in column Status has Completed.
The output will show me error for rows, 8 and 10-18,
but it should not as the values are entered at the account level.
However, there should be error for only Account4 and Account5, meaning the error should be only for Row 16 and row 18 as below -
From your updated question, I thought that the reason for your current issue might be due to that you checked columns "D" and "E". In order to achieve However, there should be error for only Account4 and Account5, meaning the error should be only for Row 16 and row 18 as below -, how about checking the columns "A", "D" and "E"? In this case, how about the following sample script?
Sample script 1:
function myFunction1() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// Process in the source sheet.
const srcSheet = ss.getSheetByName("Sheet1");
const values = srcSheet.getRange("A2:E" + srcSheet.getLastRow()).getDisplayValues();
const { errors_c } = values.reduce((o, r, i) => {
if (i == 0 || o.temp != r[0]) {
o.temp = r[0];
if (r[3] == "Completed" && !r[4]) {
o.errors_c.push(i + 2);
}
}
return o;
}, { errors_c: [], temp: "" });
// Process in the destination sheet.
const dstRange = ss.getSheetByName("Sheet2").getRange("A2");
if (errors_c.length > 0) {
dstRange.setValue("Row(s) " + errors_c.join(" and ") + " have error");
} else {
dstRange.setValue("No errors");
}
}
In this modification, the columns "A", "D" and "E" are checked.
When this script is tested to your sample input Spreadsheet, Row(s) 16 and 18 have error is put to the cell "A2" of "Sheet2".
Sample script 2:
Although unfortunately, I'm not sure whether I could correctly understand your expected result, if you want to check all values of column "D" for each "Account ID", how about the following sample script?
function myFunctionc2() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// Process in the source sheet.
const srcSheet = ss.getSheetByName("Sheet1");
const values = srcSheet.getRange("A2:E" + srcSheet.getLastRow()).getDisplayValues();
const obj = values.reduce((m, r, i) => m.set(r[0], m.has(r[0]) ? { v: [...m.get(r[0]).v, r[3]], i: m.get(r[0]).i } : { v: [r[3]], i: i + 2 }), new Map()).values();
const errors_c = [...obj].reduce((ar, { v, i }) => {
if (v.every(e => e == "Completed") && !values[i - 2][4]) {
ar.push(i);
}
return ar;
}, []);
// Process in the destination sheet.
const dstRange = ss.getSheetByName("Sheet2").getRange("A2");
if (errors_c.length > 0) {
dstRange.setValue("Row(s) " + errors_c.join(" and ") + " have error");
} else {
dstRange.setValue("No errors");
}
}
Reference:
reduce()

else statement does not return text "ID not found" google apps script

Return works if there is a match but if there is no match, the else statement doesn't work. The whole code is pasted below
function doGet(e){
return getAirportMatch(e) ;
}
function getAirportMatch(e) {
var id = e.parameter.id;
var regExp ="^.*"+id+".*|\\s{1}"+id+".*$//gm";
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1ggLOdIUES107tr_r8R-EAJpbRmZSty8JdXQJfb2U2tU/edit#gid=0");
var sheet = ss.getSheetByName("Sheet1");
var lastRow = sheet.getLastRow();
var lookupRangeValues = sheet.getRange(1, 1, lastRow, 1).getValues();
let a = []
lookupRangeValues.forEach(function (row, i){
if (row[0].match(regExp))
{
a.push(lookupRangeValues[i][0]);
}
});
if (a)return ContentService.createTextOutput(a).setMimeType(ContentService.MimeType.TEXT)
else return ContentService.createTextOutput("ID not found").setMimeType(ContentService.MimeType.TEXT);
You never enter the else statement
Reason:
Even if a is empty, it is still defined and thus the condition if (a) is always fullfilled.
You can verify this easily my modifying your text output and / or implementing logs.
Sample:
function getAirportMatch(e) {
...
let a = []
lookupRangeValues.forEach(function (row, i){
if (row[0].toString().match(regExp))
{
a.push(lookupRangeValues[i][0]);
}
});
console.log("a: " + a);
console.log("a.length: " + a.length);
if (a){
console.log("if (a)")
return ContentService.createTextOutput("a is : " + a).setMimeType(ContentService.MimeType.TEXT)
}
else {
console.log("else (a)")
return ContentService.createTextOutput("ID not found").setMimeType(ContentService.MimeType.TEXT);
}
}
Solution:
If the conditonal statement shall only evaluate to true for not empty a, rewrite the if condition as
if (a.length > 0)

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.

Sheet order not respecting index in google sheets in chrome

This spreadsheet has a script that updates the tabs and labels them in response to the week ending date entered. It also rearranges them based on the selected last day of the week (Saturday or Sunday for example). The spreadsheet formulas calculate the order of the days and the day and day of the month labelling. These are read into the script from named ranges.
It works fine on my IOS devices and sometimes works properly in chrome desktop. But (in chrome desktop) it gets into a mode where one sheet is always out of position. In the example, I label the tabs with the sequence position (0 to 6) and the returned sheet index, so it's clear that the problem is alignment between the object model and the rendering. As you can see in the following pic, the Monday sheet is out of position, if I change the end of week day again, it will be the one out of position again.
How can I force google sheets to respect the sheet index?
I tried flush but no joy. The only way I can get it to line up reliably is to close and re-open the sheet.
function onEdit(e) {
var source = e.range;
var ss = SpreadsheetApp.getActiveSpreadsheet();
if(!qualifiedSource(e.range, ["selectedDate", "lastDayOfWeek"].map(function(n) {
return ss.getRangeByName(n);
}))) return;
var fmtedNames = WeekEndingDate2();
// add indexing to the sheet names
ss
.getSheets()
.filter(function(sht) { return fmtedNames.indexOf(sht.getName()) != -1 })
.forEach(function (s, i) { s.setName(s.getName() + ":" + i + ":" + s.getIndex()); });
}
function qualifiedSource (source /*range*/, target /*range | range[]*/) {
if(!isArray(target)) target = [target];
return target.some(function(t) {
return source.getSheet().getName() == t.getSheet().getName() && source.getA1Notation() == t.getA1Notation();
});
}
function WeekEndingDate2() {
var _wb = SpreadsheetApp.getActiveSpreadsheet();
var _days = _wb.getRangeByName("daysOfWeek");
var _daysFmt = _wb.getRangeByName("daysOfWeekFmt");
return (function _update() {
var daySheets = SheetsCollection();
var days = _days.getValues()[0];
var daysFmt = _daysFmt.getValues()[0];
daySheets
.Add(days.map(namesToSheets))
.Sort(daysFmt);
return daysFmt;
function namesToSheets(d, i) {
var allSheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var targetSheet;
allSheets.some(function(sht) {
return targetSheet = sht.getName().indexOf(d) === 0 ? sht : null;
});
Logger.log(Utilities.formatString("%s\t%s", d, targetSheet.getName()));
if (targetSheet == null)
throw new Error("Error: Missing sheet for " + d);
return targetSheet.setName(daysFmt[i]);
}
})();
}
function SheetsCollection () {
var _sheets = {}; // hash of WrappedSheets
var _maxIndex = 0;
function _hash (n) {
return n.replace(" ", "_");
}
function _addItem (s /*worksheet*/) {
_sheets[_hash(s.getName())] = WrappedSheet(s);
_maxIndex = Math.max(_maxIndex, s.getIndex())
}
function _add (s /*worksheet | worksheet[]*/) {
if(Array.isArray(s))
s.forEach(_addItem);
else
_addItem(s);
return this;
}
function _sort ( sortOrder /*range | string[]*/, delay /* int milliseconds */) {
var sortedNames = sortOrder.getValues ? sortOrder.getValues()[0] : sortOrder;
var namesLength = sortedNames.length;
var i, sht;
for each (var name in sortedNames) {
Logger.log(name);
_sheets[_hash(name)].MoveTo(_maxIndex);
if(delay) Utilities.sleep(delay);
}
return this;
}
return {
Add: _add,
Sort: _sort
}
}
function WrappedSheet(sheet /*string || sheet*/) {
var _wb = SpreadsheetApp.getActive();
var _sheets = _wb.getSheets();
var _shtName = typeof sheet == "string" ? sheet : sheet.getName();
function _moveTo (to /*integer*/) {
var insertAt = to;
var actSht = _wb.getActiveSheet();
var maxAttempts = 10;
var attempt = 1;
var before = this.Sheet.getIndex();
Logger.log(listSheets(""));
_wb.setActiveSheet(this.Sheet);
_wb.moveActiveSheet(insertAt);
_wb.setActiveSheet(actSht);
Logger.log("%s -> %s after %s", this.Name, this.Sheet.getIndex(), attempt -1);
Logger.log(listSheets(""));
}
return {
MoveTo: _moveTo,
get Sheet() { return _wb.getSheetByName(_shtName); },
get Position() { return _wb.getSheetByName(_shtName).getIndex(); },
Name: _shtName
}
}
I faced the exact same problem as you with a very similar functioning script. Unfortunately, there is no 'fix' for this issue, it seems to be a caching issue with the browser. The only way around it I found was to refresh the window twice or close and reopen the spreadsheet and then the tabs appear in the correct order.

Bulk Update users G-suite

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.