I have set up an email to Google Sheets automation, where contents from a specific email automagically become new rows in a Google sheet. But one of the values is a full address (which is always one of only two addresses), and I would like it to be changed automatically to just the place name. So "address No.1" to "Place Name No.1", and "address No.2" to "Place Name No.2".
The text is always in column E, and added to a new row.
Here is what I tried to use:
function onOpen() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("e1:e10");
var to_replace = "full long address";
var replace_with = "place name";
replaceInSheet(sheet,range, to_replace, replace_with);
}
function replaceInSheet(sheet, range, to_replace, replace_with) {
//Confirm
var ui = SpreadsheetApp.getUi();
var spread = SpreadsheetApp.getActiveSpreadsheet();
var result = ui.alert(
"Will update " + to_replace + " to " + replace_with + " ",
'Are you sure you want to continue?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
spread.toast("Will update " + to_replace + " to " + replace_with + " ", "ALERT");
var data = range.getValues();
var oldValue="";
var newValue="";
var cellsChanged = 0;
for (var row=0; row<data.length; row++) {
for (var item=0; item<data[row].length; item++) {
oldValue = data[row][item];
newValue = data[row][item].replace(to_replace, replace_with);
if (oldValue!=newValue)
{
cellsChanged++;
data[row][item] = newValue;
}
}
}
range.setValues(data);
spread.toast(cellsChanged + " cells changed", "STATUS");
}
else {
// User clicked "No" or X in the title bar.
spread.toast("No action taken", "ABANDONED");
}
}
The OP's original code was most elegant (sourced from webapps) but over complicated for the task, and also not adapted to the replacing two addresses.
The following code is essentially a cut-down version of the OP original. A number of Logger.log() statements have been kept which will allow the OP (if &/or when necessary) to test values at different stages of the code.
The code logic is straightforward.
1) the addresses to be found and replaced are described as variables (var address01find and var address01replace, rinse and repeat for address02). The OP can edit this according to their taste.
2) get the last row in column E.
3) get the values for column E.
4) iterate through the values, row-by-row, testing for a value equal to either address01find or address02find. If the value is found, replace the value with address01replace and address02replace respectively.
5) after the loop, setValues for the entire data range. Most field values won't change, but those fields that were modified during the loop, will be updated to the revised value.
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var MenuEntries = [{
name: "Replace addresses",
functionName: "replaceaddresss"
}];
ss.addMenu("Address Update", MenuEntries);
};
function replaceaddresss() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//Logger.log("DEBUG: sheet name: "+sheet.getSheetName());//DEBUG
//Logger.log("DEBUG: range: "+range.getA1Notation());//DEBUG
var Evals = ss.getRange("E1:E").getValues();
var Elast = Evals.filter(String).length;
//Logger.log("DEBUG: last row in E: "+Elast);//DEBUG
var range = sheet.getRange(1, 5, Elast)
//Logger.log("DEBUG: range: "+range.getA1Notation());//DEBUG
var columnValues = range.getValues();
var address01find = "Full Address 123, Tokyo, Japan";
var address01replace = "Place Name No.1";
var address02find = "Short Address 123, Tokyo, Japan";
var address02replace = "Place Name No.2";
//Logger.log("DEBUG: address #1: find: "+address01find+", replace with: "+address01replace);//DEBUG
//Logger.log("DEBUG: address #2: find: "+address02find+", replace with: "+address02replace);//DEBUG
for (i = 0; i < Elast; i++) {
if (columnValues[i][0] === address01find) {
columnValues[i][0] = address01replace;
}
if (columnValues[i][0] === address02find) {
columnValues[i][0] = address02replace;
}
};
range.setValues(columnValues);
}
Before and after
UPDATE for OnEdit
This update looks at the scenario where data (i.e. the street address in Column E) is created by a script.
The code is essentially the same except that the event range is known from onEdit, and it is only necessary to get the values, evaluate the addresses, and update the data IF the address value was changed.
Note: this script requires that it be created as an Installable OnEdit Trigger.
function oneditemailupdate(e) {
// set up spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// setup triggerdetector
var trigger = 0;
// User variables
// identify the column to watch
var addresscol = 5; // = Column E - this is the column to watch
// Setup the old and new addresses
var address01find = "Full Address 123, Tokyo, Japan";
var address01replace = "Place Name No.1";
var address02find = "Short Address 123, Tokyo, Japan";
var address02replace = "Place Name No.2";
// list onedit values for debug reporting and other use
var debug_e = {
authMode: e.authMode,
range: e.range.getA1Notation(),
source: e.source.getId(),
user: e.user,
value: e.value,
oldValue: e.oldValue
};
//Logger.log("DEBUG: AuthMode: "+debug_e.authMode);// DEBUG
//Logger.log("DEBUG: Range: "+debug_e.range);// DEBUG
//Logger.log("DEBUG: Source: "+debug_e.source);// DEBUG
//Logger.log("DEBUG: User: "+debug_e.user);// DEBUG
//Logger.log("DEBUG: user email"+debug_e.user.getEmail());// DEBUG
//Logger.log("DEBUG: Value: "+debug_e.value);// DEBUG
//Logger.log("DEBUG: Old value: "+debug_e.oldValue);// DEBUG
//Logger.log("DEBUG: AuthMode: "+debug_e.authMode+", Range: "+debug_e.range+", source: "+debug_e.source+", user: "+debug_e.user+", value: "+debug_e.value+", old value: "+debug_e.oldValue);//DEBUG
// get the values for the range that was edited (e.range)
var emailvalues = e.range.getValues();
// evaluate email address value#1 and change value if necessary
if (emailvalues[0][4] === address01find) {
emailvalues[0][4] = address01replace;
// set the trigger so that the range value can be updated
trigger = 1;
//Logger.log("DEBUG: Updated address01");//DEBUG
}
// evaluate email address value#2 and change value if necessary
if (emailvalues[0][4] === address02find) {
emailvalues[0][4] = address02replace;
// set the trigger so that the range value can be updated
trigger = 1;
//Logger.log("DEBUG: Updated address02");//DEBUG
}
// if trigger value has been set to one, then update the values
// Logger.log("DEBUG: Trigger value is "+trigger);//DEBUG
if (trigger == 1) {
e.range.setValues(emailvalues);
}
}
UPDATE - FORM SUBMIT
Mea culpa. I managed to ignore the fact that the OP said that the spreadsheet was an email response sheet. Script executions and API requests do not cause triggers to run. So onEdit (whether as a Simple or Installable trigger) will not work.
The script needs to be installed as an Installable Script
The event type = "On form Submit"
This will return 'Form Submit' event data when the event object is called.
Note: the sheet name is now a variable. Otherwise the code is almost (but not quite) identical to the previous onEdit version.
function addressupdate(e) {
// set up spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formresponsesheet = "Form Responses 2";
var sheet = ss.getSheetByName(formresponsesheet);
// setup changetriggerdetector
var trigger = 0;
// User variables
// identify the column to watch
var addresscol = 5; // = Column E - this is the column to watch
// Setup the old and new addresses
var address01find = "Full Address 123, Tokyo, Japan";
var address01replace = "Place Name No.1";
var address02find = "Short Address 123, Tokyo, Japan";
var address02replace = "Place Name No.2";
// list onedit values for debug reporting and other use
var debug_e = {
authMode: e.authMode,
range: e.range.getA1Notation(),
namedValues: e.namedValues,
triggerUid: e.triggerUid,
values: e.values
};
//Logger.log("DEBUG: AuthMode: "+debug_e.authMode);// DEBUG
//Logger.log("DEBUG: Range: "+debug_e.range);// DEBUG
//Logger.log("DEBUG: named ValuesSource: "+debug_e.namedValues);// DEBUG
//Logger.log("DEBUG: triggerUid: "+debug_e.triggerUid);// DEBUG
//Logger.log("DEBUG: values: "+debug_e.values);// DEBUG
// get the values for the range that was created (e.range)
var emailvalues = e.range.getValues();
// evaluate email address value#1 and change value if necessary
if (emailvalues[0][4] === address01find) {
emailvalues[0][4] = address01replace;
// set the trigger so that the range value can be updated
trigger = 1;
//Logger.log("DEBUG: Updated address01");//DEBUG
}
// evaluate email address value#2 and change value if necessary
if (emailvalues[0][4] === address02find) {
emailvalues[0][4] = address02replace;
// set the trigger so that the range value can be updated
trigger = 1;
//Logger.log("DEBUG: Updated address02");//DEBUG
}
// if trigger value has been set to one, then update the values
// Logger.log("DEBUG: Trigger value is "+trigger);//DEBUG
if (trigger == 1) {
e.range.setValues(emailvalues);
}
}
Related
I am using a snapData script sourced externally and shown below:
function snapData() {
// get current sheet and tabs
var ss = SpreadsheetApp.getActiveSpreadsheet(); var current = ss.getSheetByName('Fees (management)'); var database = ss.getSheetByName('Fees (data)');
// count rows to snap var current_rows = current.getLastRow();
var database_rows = database.getLastRow() + 1;
var database_rows_new = current_rows + database_rows - 2;
var rows_new = current.getRange("A2:F" + current_rows).getValues();
// snap rows, can run this on a trigger to be timed
database.getRange("A" + database_rows + ":F" + database_rows_new).setValues(rows_new);
}
At the moment, it is triggered by pressing the 'Run' button, however I would like to have the script triggered by a new row appearing on another tab.
Help with this would be appreciated. Thanks!
"I would like to have the script triggered by a new row appearing on another tab."
By using the installable trigger onChange, it is possible to trigger the script as planned. source is an Event Object returned by onChange, though it is not included in the documnentation.
var sheetname = src.getActiveSheet().getSheetName(): get the sheet name
var ctype = e.changeType;: get the change type
if (sheetname === "target" && ctype === "INSERT_ROW"){: test if the type was inserting a row on "target"
function so5899606501(e) {
// return all the event objects
Logger.log(JSON.stringify(e));
// get the source
var src = e.source;
var spreadsheetname = src.getName();
var sheetname = src.getActiveSheet().getSheetName()
Logger.log("spreadsheet = "+spreadsheetname+", sheet name = "+sheetname);
var currentcell = src.getCurrentCell();
Logger.log("the current cell = "+currentcell.getA1Notation());
// get the change type
var ctype = e.changeType;
Logger.log("the change type is "+ctype);
// test for the type of change
if (ctype == "INSERT_ROW"){
Logger.log("A row was just inserted");
}
else
{
Logger.log("the change type wasnt a new row, it was "+ctype)
}
//test for the sheet where the change took place
if (sheetname == "target"){
Logger.log("the change took place on the sheet named 'target'");
}
else
{
Logger.log("the change took place on the sheet named" +sheetname+".")
}
// test for the change type AND the sheet name
if (sheetname == "target" && ctype == "INSERT_ROW"){
Logger.log("Eureka!. A new row was inserted on the sheet named 'target. Lets do stuff");
}
else{
Logger.log("Sigh. It either wasn't a new row AND/OR it wasn't on the sheet named 'target'. Either way, we can ignore it.");
}
}
I have read a lot of onEdit and Triggers script but still I cannot achieve what I want to achieve, I code a lot in excel VBA and google sheet is very different. So the thing is based on my screenshot, what I want is to send an email once the cell contains "Approved", "denied", "In progress" and the email address must be based on the parallel of the edited cell. I'm dying to get this work done.
The code is based on the internet but I cannot tweak it based on my data/sheet.
You can add a custom function to a dropdown menu in the Spreadsheets UI with the following script. This will allow you to circumvent the onEdit() restriction that doesn't allow one to use the MailApp class, but it is at the cost of having users manually call the script instead of the script running automatically.
Here the user will select "Send E-Mail" from the dropdown menu, and it will prompt him/her for the Primary Key via an input prompt modal. The row of the corresponding key will be identified and an e-mail sent out after status is automatically changed to "approved". This script assumes that the spreadsheet contains at least four columns with header rows "Primary Key", "Description", "Email", and "Status" in any order.
Please note: this code was tested successfully. Please update lines 20 and 21 by replacing the square brackets and text contained therein that defines sheetURL and workSheetName variables.
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Custom Menu')
.addItem('Send E-Mail', 'sendEmail')
.addToUi();
}
function sendEmail(){
// Display a dialog box with a title, message, input field, and "OK" and "Cancel" buttons. The
// user can also close the dialog by clicking the close button in its title bar.
var ui = SpreadsheetApp.getUi();
var response = ui.prompt('Pop-Up Prompt', 'Please enter primary key:', ui.ButtonSet.OK_CANCEL);
// Process the user's response.
if (response.getSelectedButton() == ui.Button.OK) {
Logger.log('The user entered the following primary key:', response.getResponseText());
// Map the header rows in order that column position is not hard-coded.
var sheetURL = '[ENTER YOUR SHEET URL HERE]';
var workSheetName = '[ENTER YOUR WORKSHEET NAME HERE]';
var sheet = SpreadsheetApp.openByUrl(sheetURL).getSheetByName(workSheetName);
var lastColumn = sheet.getLastColumn();
var headerRange = sheet.getRange(1, 1, 1, lastColumn);
var headers = headerRange.getValues();
for (var i=1; i<headers[0].length+1; i++) {
switch (headers[0][i-1]){
case "Primary Key":
var primaryKeyIndex = i;
break;
case "Description":
var descriptionIndex = i;
break;
case "Email":
var emailIndex = i;
break;
case "Status":
var statusIndex = i;
break;
}
}
// Header rows mapped.
// Search for row corresponding to primary key.
var primaryKey = response.getResponseText();
var keyRow = findInColumn(columnToLetter(primaryKeyIndex), primaryKey);
if (keyRow == -1){
ui.alert('Primary Key "'+ primaryKey + '" not found.');
} else {
ui.alert('Primary Key "'+ primaryKey + '" found at row: ' +keyRow+ '.');
sheet.getRange(keyRow, statusIndex).setValue("Approved");
//Prepare Email
var subject = "test";
var email = sheet.getRange(keyRow, emailIndex).getValue();
var body = "Hi, \n\n Your entry with primary key " + primaryKey + " is now approved.";
MailApp.sendEmail(email, subject, body);
}
} else if (response.getSelectedButton() == ui.Button.CANCEL) {
Logger.log('The user clicked cancel.');
} else {
Logger.log('The user clicked the close button in the dialog\'s title bar.');
}
}
// Helper function to find corresponding row to data in column.
function findInColumn(column, data) {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var column = sheet.getRange(column + ":" + column); // like A:A
var values = column.getValues();
var row = 0;
while ( String(values[row]) && String(values[row][0]) !== String(data) ) {
row++;
}
if (String(values[row][0]) === String(data))
return row+1;
else
return -1;
}
// Helper function to convert Column Number to Column Letter
function columnToLetter(column){
var temp, letter = '';
while (column > 0)
{
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
I'd suggest that you not use the onEdit trigger for sending email. I think it's over used by many users. If you are, you will have to go with the installable triggers. This is an example email solution that looks pretty clean that came in yesterday.
You can use most of this code below. Modify the email portions to suit your needs.
This code checks for sheet name to be 'Form Responses' and edited column header to be 'Status' as from pics given above.
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var aSheet = ss.getActiveSheet();
// check sheet name
if (aSheet.getName() != 'Form Responses') return;
var range = ss.getActiveRange();
var row = range.getRow();
var col = range.getColumn();
// Logger.log(col);
var headers = aSheet.getRange(1,1,1,aSheet.getLastColumn()).getValues()[0];
// Logger.log(headers[col-1]);
// check column header
if (headers[col-1] != 'Status') return;
var value = range.getValue();
var values = ["approved", "denied", "in progress"]; // values to check for
// check values
if (values.indexOf(value.toString().toLowerCase()) == -1) return;
// Logger.log(row);
var rowValues = aSheet.getRange(row, 1, 1, aSheet.getLastColumn()).getValues()[0];
Logger.log(rowValues);
// change as necessary
var recipient = rowValues[1]; // email is in 2nd column
var body = 'Email body'; // create body
var subject = 'Test'; // set subject
// send email
MailApp.sendEmail(recipient, subject, body);
}
I'm getting this error while running this sheet.
Cell reference out of range (line 81, file "genreportSE")
I don't know why it says it's 'out of range'.
I tried to used 'copyvalues'. I saw a script where you can't really "print" a range, but you can create another spreadsheet, copy that range, then print that sheet and delete it.
How should I accomplish this?
function genreportSE() { // This function let us read the value of a cell from a sheet and change the value of another cell in a different sheet
var ss = SpreadsheetApp.getActive(); //ss stands for spreadsheet, this is the active spreadsheet
var clientsheet = ss.getSheetByName('Clientes SE');
var gensheet = ss.getSheetByName('Generador SE');
var clienttable = clientsheet.getDataRange();
var numberofservices = clienttable.getNumRows(); //The number of services in the Clientes sheet
var error1;
var error2;
var rangetocheck1;
var rangetocheck2;
var client;
var clientname;
var i=0;
var reportswitherrors = []; //Array for faulty reports
var email ='jvaldez#galt.mx';
var subject = "Reporte de producción y consumo - " + (new Date()).toString();
var body = "TEXT" ;
for (i=0;i<=2;i++){
gensheet.getRange('B2').setValue(clientsheet.getRange(i+2,1).getValue()); //This will change the cell "B2" in "Generador SE" to the current service number for the report generation
Utilities.sleep(3000); //A timer to let the importdata function get the data from the SE server in miliseconds
client = gensheet.getRange('B4').getValue;
clientname = String(client);
rangetocheck1 = gensheet.getRange('B8:C14').getValues(); //Data range that could present calculation errors ********
rangetocheck2 = gensheet.getRange('H8:H14').getValues(); //Data range that could present calculation errors ********
if(String(rangetocheck1).indexOf('#N/A') == -1) { //This checks if there are any errors in rangetocheck1
error1 = false;
} else {
error1 = true;
};
if(String(rangetocheck2).indexOf('#N/A') == -1) { //This checks if there are any errors in rangetocheck2
error2 = false;
} else{
error2 = true;
};
if(error1||error2){
reportswitherrors.push(clientsheet.getRange(i+2,1).getValue()); //This appends the current service number to the faulty services array
} else {
// Convert individual worksheets to PDF
var newSpreadsheet = SpreadsheetApp.create("Spreadsheet to export",15,60);
newSpreadsheet.getSheetByName('Sheet1').activate();
var newsheet = newSpreadsheet.getSheetByName('Sheet1');
var genRange = gensheet.getRange('A1:H50').copyValuesToRange(newsheet,0,10,0,55)
var pdf = DriveApp.getFileById(newSpreadsheet.getId()).getAs('application/pdf').getBytes();
var attach = {fileName:'Weekly Status.pdf',content:pdf, mimeType:'application/pdf'};
MailApp.sendEmail(email, subject, body, {attachments:[attach]});
DriveApp.getFileById(newSpreadsheet.getId()).setTrashed(true);
}
};
Logger.log(reportswitherrors);
}
It appears that you've got your row & column dimensions flipped between function calls. (Because Google decided to be inconsistent with the order of them...)
This line calls create(name, rows, columns):
var newSpreadsheet = SpreadsheetApp.create("Spreadsheet to export",15,60);
You've created a spreadsheet with 15 rows and 60 columns.
A bit further along, probably on line 81, copyValuesToRange(sheet, column, columnEnd, row, rowEnd) gets invoked:
var genRange = gensheet.getRange('A1:H50').copyValuesToRange(newsheet,0,10,0,55)
I have a GAS script run from a Google Sheet with two functions. The first function downloads calendar event details in a named calendar between two dates. The name of the calendar, the start date and the end date are read in from cells A2, B2, and C2 respectively. This function is working fine:
//Function to list certain events in your GCalendar.
function listMyEvents() {
var sheet = SpreadsheetApp.getActiveSheet();
var calid = sheet.getRange("A2").getValue();
if(calid == null) {
SpreadsheetApp.getActiveSpreadsheet().toast('Please enter a valid calendar name!');
return;
}
var startTime = sheet.getRange("B2").getValue();
if(startTime == 0) {
SpreadsheetApp.getActiveSpreadsheet().toast('Please enter a start date!');
return;
}
if(isNaN(startTime)) {
SpreadsheetApp.getActiveSpreadsheet().toast('Please enter a valid start date!');
return;
}
var endTime = sheet.getRange("C2").getValue();
if(endTime == 0) { //If the cell doesn't contain anything send an error prompt
SpreadsheetApp.getActiveSpreadsheet().toast('Please enter an end date');
return;
}
if(isNaN(endTime)) { //If the cell holds text rather than a number then send an error prompt
SpreadsheetApp.getActiveSpreadsheet().toast('Please enter a valid end date!');
return;
}
var calendar = CalendarApp.getCalendarsByName(calid)[0]; //get the calendar name
var events = calendar.getEvents(startTime, endTime);
var data; //declare a variable to hold all the information.
for(var i = 0; i < events.length;i++){
var event = events[i];
data = [
event.getTitle(),
event.getDescription(),
event.getStartTime(),
event.getEndTime(),
event.getLocation(),
event.getId(),
"N"
];
sheet.appendRow(data);
}
sheet.autoResizeColumn(1);
sheet.autoResizeColumn(2);
sheet.autoResizeColumn(3);
sheet.autoResizeColumn(4);
sheet.autoResizeColumn(5);
sheet.autoResizeColumn(6);
sheet.autoResizeColumn(7);
}
The second function tries to read in the event Id from column F, in order to allow individual events to be amended within the sheet before being written back to Google Calendar. However, the script errors on the line calendarEvent.getId() with the error Cannot call method "getId" of undefined. (line 118, file "Code").
I did read that you need to use the .getEventSeriesById() method - but the events aren't in a series! Is it possible to amend individual events with a script or do I need to delete all events and recreate them with amendments? My code for the second function is here:
function amendCalEvents() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 6; // First row of data to process
var numRows = sheet.getLastRow(); // Number of rows to process
//var maxRows = Math.min(numRows,200); //Limit the number of rows to prevent enormous number of event creations
var calid = sheet.getRange("A2").getValue(); //putting the calendar name in double quotes creates double entries. Who knew?
var dataRange = sheet.getRange(startRow, 1, numRows, 7); //startRow, startCol, endRow, endCol
var data = dataRange.getValues();
var cal = CalendarApp.getCalendarsByName(calid)[0]; //get the calendar name
for (i in data) {
var row = data[i];
var title = row[0]; // column A
var desc = row[1]; // column B
var tstart = row[2]; // column C
var tstop = row[3]; // column D
var loc = row[4]; // column E
var evId = row[5]; // column F
var evDelFlag = row[6]; // column G
var event = cal.calendarEvent.getId(evId);
if (evDelFlag == "N" || evDelFlag == "n") {
event.setTitle(title);
event.setDescription(desc);
event.setTime(tstart, tstop);
event.setLocation(loc);
} else {
cal.getId(evId).deleteEvent;
}
}
}
Any help or pointers are most gratefully accepted.
There are some changes you can do in the function amendCalEvents
1.- numRows variable, you are setting the value of getLasRow but you are not subtracting the rows used for the first information (e.g. if you have just 1 calendar and this calendar has one event, when you run the first function it'll add a new row and this variable will have 3 as value.
so when you u call the function getRange, it will bring the information from 3 rows and not just the one that has the information.
2.- a workaround of using the getEventSeriesById, you can probably call again the function getEvents as you did in the first fucntion.
As you are already looping in the for with a similar amount of data you could access the element inside the array o events and set the desired values.
var event = events[i];
3.- to delete the event now you can call:
event.deleteEvent();
Hope this helps
I'm attempting to set up yearly recurring events from a Google spreadsheet, and although I've followed this answer, I still can't get the ID to write to each row and thus allow me to write and check entries.
The column layout is
RenDate | Domain | Client | Type | Registrar | ID
Code:
/**
* Adds a custom menu to the active spreadsheet, containing a single menu item
* for invoking the exportEvents() function.
* The onOpen() function, when defined, is automatically invoked whenever the
* spreadsheet is opened.
* For more information on using the Spreadsheet API, see
* https://developers.google.com/apps-script/service_spreadsheet
*/
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Export Events",
functionName : "exportEvents"
}];
sheet.addMenu("Calendar Actions", entries);
};
/**
* Export events from spreadsheet to calendar
*/
function exportEvents() {
var sheet = SpreadsheetApp.getActiveSheet();
var headerRows = 1; // Number of rows of header info (to skip)
var range = sheet.getDataRange();
var data = range.getValues();
var calId = "[id removed]#group.calendar.google.com";
var cal = CalendarApp.getCalendarById(calId);
for (i in data) {
if (i < headerRows) continue; // Skip header row(s)
var row = data[i];
var date = new Date(row[0]); // First column - Renewal Date
var domain = row[1]; // Second column - Domain
var client = row[2]; // Third column - Client
var type = row[3]; // Fourth column - Type
var source = row[4]; // Fifth column - Registrar
var id = row[5]; // Sixth column - ID
if(row[1]=="") continue; // Skip rows if no date exists
var title = type + " renewal for " + domain;
var desc = "Renew with " + source + " for " + client;
// Check if event already exists, update it if it does
try {
var event = cal.getEventSeriesById(id);
event.setTitle('got you'); // this is to "force error" if the event does not exist
}catch (e) {
var newEvent = cal.createEvent(title, date, {description:desc});
row[5] = newEvent.getId(); // Update the data array with event ID
Logger.log('event created'); // For debugging
var event = cal.getEventSeriesById(row[5]);
}
event.setTitle(title);
event.setDescription(desc);
var recurrence = CalendarApp.newRecurrence().addYearlyRule();
event.setRecurrence(recurrence, date);
debugger;
data[i] = row ;
}
// Record all event IDs to spreadsheet
range.setValues(data);
}
Debugging seems to be fine, but no events are created. I was able to create events using the CreateAllDayEventSeries function, but that seems to be impossible to work out with the ID approach, so that's why I'm trying this method.
Edit: Made change to continue function as caught by Serge insas.
Now the script is not finding the method for createEvent on the catch command.
I found that you were making a few errors (minimal) that prevented it to work.
One detail : you tried to create events with a single date, this works only for allDay Events so I guess that's what you wanted to do.
below is the full working code :
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Export Events",
functionName : "exportEvents"
}];
sheet.addMenu("Calendar Actions", entries);
};
/**
* Export events from spreadsheet to calendar
*/
function exportEvents() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var data = range.getValues();
var headers = data.shift();
Logger.log(data);
var calId = "h22nevo15tm0nojb6ul4hu7ft8#group.calendar.google.com";
var cal = CalendarApp.getCalendarById(calId);
for (i in data) {
var row = data[i];
Logger.log(row);
var date = new Date(row[0]); // First column - Renewal Date
var domain = row[1]; // Second column - Domain
var client = row[2]; // Third column - Client
var type = row[3]; // Fourth column - Type
var source = row[4]; // Fifth column - Registrar
var id = row[5]; // Sixth column - ID
if(row[0]=="") continue; // Skip rows if no date exists
var title = type + " renewal for " + domain;
var desc = "Renew with " + source + " for " + client;
// Check if event already exists, update it if it does
try {
var event = cal.getEventSeriesById(id);
event.setTitle('got you'); // this is to "force error" if the event does not exist
}catch (e) {
var newEvent = cal.createAllDayEvent(title, date, {description:desc});
row[5] = newEvent.getId(); // Update the data array with event ID
Logger.log('event created'); // For debugging
var event = cal.getEventSeriesById(row[5]);
}
event.setTitle(title);
event.setDescription(desc);
var recurrence = CalendarApp.newRecurrence().addYearlyRule();
event.setRecurrence(recurrence, date);
debugger;
data[i] = row ;
}
// Record all event IDs to spreadsheet
sheet.getRange(2,1,data.length,data[0].length).setValues(data);
}
I was able to resolve the issue on my own (although thanks again to Serge for some excellent assistance initially and a solid answer, even though I didn't see it before I fixed the issue). Here's the code I eventually settled with:
/**
* Adds a custom menu to the active spreadsheet, containing a single menu item
* for invoking the exportEvents() function.
* The onOpen() function, when defined, is automatically invoked whenever the
* spreadsheet is opened.
* For more information on using the Spreadsheet API, see
* https://developers.google.com/apps-script/service_spreadsheet
*/
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Export Events",
functionName : "exportEvents"
}];
sheet.addMenu("Calendar Actions", entries);
};
/**
* Export events from spreadsheet to calendar
*/
function exportEvents() {
var sheet = SpreadsheetApp.getActiveSheet();
var headerRows = 1; // Number of rows of header info (to skip)
var range = sheet.getDataRange();
var data = range.getValues();
var calId = "[id withheld]#group.calendar.google.com";
var cal = CalendarApp.getCalendarById(calId);
for (i in data) {
if (i < headerRows) continue; // Skip header row(s)
var row = data[i];
var date = new Date(row[0]); // First column - Renewal Date
var domain = row[1]; // Second column - Domain
var client = row[2]; // Third column - Client
var type = row[3]; // Fourth column - Type
var source = row[4]; // Fifth column - Registrar
var id = row[5]; // Sixth column - ID
if(row[0]=="") continue; // Check to see if date exists
var title = type + " renewal for " + domain;
var desc = "Renew with " + source + " for " + client;
// Check if event already exists, update it if it does
try {
var event = cal.getEventSeriesById(id);
}catch(e){
//Do nothing
}
if(!event){
var newEvent = cal.createAllDayEventSeries(title, date, CalendarApp.newRecurrence().addYearlyRule(), {description:desc});
row[5] = newEvent.getId(); // Update the data array with event ID
Logger.log('Event ID ' + row[5] + ' created'); // For debugging
}
else{
var event = cal.getEventSeriesById(row[5]);
event.setTitle(title);
event.setDescription(desc);
Logger.log('Event ID ' + row[5] + ' already exists'); //For debugging
}
debugger;
data[i] = row ;
}
// Record all event IDs to spreadsheet
range.setValues(data);
}