See code below: It's set to update on calendar event.
But events are still duplicating onto the sheet.
Can you take a look and see where I went wrong?
get only new created events - is there a way to getevents by created date (now)?
}
var events = calendar.getEvents(start, end);
var eventDetails = [];
var eventarray = new Array();
for(var i = 0; i<events.length; i++){
eventDetails.push([events[i].getLocation(), events[i].getTitle(), events[i].getDescription(), events[i].getStartTime()]);
}
var startRow = sheet.getLastRow();
var startCol = 2;
for(var j = 0; j<eventDetails.length; j++){
var tempRange = sheet.getRange(startRow+j,startCol, 1, 4);
var eventArray = new Array(eventDetails[j]);
tempRange.setValues(eventArray);
}
//Here lies the problem code//
for(i in eventDetails){
var row = eventDetails[i];
var duplicate = false;
for(j in eventArray){
if(row.slice(0,2).join() == eventArray[j].slice(0,2).join()){
duplicate = true;
}
}
}
if(!duplicate){
eventArray.push(row);
{
return eventArray;
ss.sort(2, true);
}
}
}
Issue:
You have a sheet with data from a list Calendar events.
You want a script to retrieve and write any new events, ignoring the ones already in the sheet.
You identify an event through its location, title and description.
Solution:
If all this is correct, then you can do the following:
After retrieving the events from the calendar, use map() to retrieve the event details.
If there are any old events in the sheet, filter them out of your eventDetails array, using filter() and every() (the second parameter of slice() is exclusive so, if you want to compare the three first properties, it should be slice(0,3) instead of slice(0,2)).
If the filtered array of eventDetails is not empty (that is, there are new events in the Calendar), write those new events to your sheet.
Code sample:
function importCalendar(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1'); //Target sheet for events
var calendarName = sheet.getRange('C2').getValue(); //name of calendar ex. calendar#gmailcom
var start = sheet.getRange('C3').getValue(); //to date
var end = sheet.getRange('C4').getValue(); //from date
var calendar = CalendarApp.getCalendarById(calendarName);
if(!calendar) calendar = CalendarApp.getCalendarsByName(calendarName)[0];
var events = calendar.getEvents(start, end);
var eventDetails = events.map(event => [event.getLocation(), event.getTitle(), event.getDescription(), event.getStartTime()]);
var lastRow = sheet.getLastRow();
var startCol = 2; // Column where the event list starts
var oldStartRow = 8; // Row where the event list starts
var numCols = 4; // Number of event fields
var numRows = lastRow - oldStartRow + 1;
if (numRows !== 0) { // If there are events in the sheet, filter the duplicates
var oldEvents = sheet.getRange(oldStartRow, startCol, lastRow - oldStartRow + 1, numCols).getValues();
// Filter out duplicates:
eventDetails = eventDetails.filter(eventRow => oldEvents.every(oldEvent => oldEvent.slice(0,3).join() != eventRow.slice(0,3).join()));
}
if (eventDetails.length != 0) { // Check if there is any new event coming from the Calendar
sheet.getRange(lastRow + 1, startCol, eventDetails.length, eventDetails[0].length).setValues(eventDetails);
}
}
Related
Absolute noob here !
Background:
am trying to create a Google Sheet which I can update for a series of events and
create Google Calendar events based on those entries
so far, am successful in creating calendar events and also updating back the last column of the sheet with the EventID (iCalUID) - thanks to other stackoverflow posts
am also successful in not creating Duplicates by checking if the EventID (iCalUID) is already present in the last column - thanks again to other stackoverflow posts
But... have another requirement, where am failing:
need to mark an existing event as 'Cancelled' in one of the columns in the sheet and
if this is 'true' then look-up the EventID (iCalUID) from the corresponding last cell (of that row which has a 'Cancelled' entry) and
delete that particular event from the calendar
also, calendar events should NOT be created again as long as that cell remains/retains the word 'Cancelled'.
the "var check1 = row[23]; //Booked/Blocked/Cancelled" in below script was just added to bring in this logic that I wanted, but am kind of unable to proceed
Relevant screen-shot of the sheet
Code that I used so far as below:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Sync to Calendar')
.addItem('Sync Now', 'sync')
.addToUi();
}
function sync() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var calendar = CalendarApp.getCalendarById('myemailid#gmail.com');
var startRow = 2; // First row from which data should process > 2 exempts my header row
var numRows = sheet.getLastRow(); // Number of rows to process
var numColumns = sheet.getLastColumn();
var dataRange = sheet.getRange(startRow, 1, numRows-1, numColumns);
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var name = row[1]; //Name of Guest
var place = row[4]; //Add2
var room = row[9]; //Room Number
var inDate = new Date(row[10]); //Check-In Date
var outDate = new Date(row[11]); //Check-Out Date
var check1 = row[23]; //Booked/Blocked/Cancelled
var check2 = row[24]; //Event created and EventID (iCalUID) populated
if (check2 == "") {
var currentCell = sheet.getRange(startRow + i, numColumns);
var event = calendar.createEvent(room, inDate, outDate, {
description: 'Booked by: ' + name + ' / ' + place + '\nFrom: ' + inDate + '\nTo: ' + outDate
});
var eventId = event.getId();
currentCell.setValue(eventId);
}
}
}
I believe your goal is as follows.
You want to check the columns "X" and "Y".
When the column "X" is not Cancelled and the column "Y" is empty, you want to create a new event.
When the column "X" is Cancelled and the column "Y" is not empty, you want to delete the existing event.
When the column "X" is Cancelled, you don't want to create a new event.
In this case, how about the following modification?
Modified script:
In this script, in order to check whether the event has already been deleted, Calendar API is used. So please enable Calendar API at Advanced Google services.
function sync() {
var calendarId = 'myemailid#gmail.com'; // Please set your calendar ID.
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var calendar = CalendarApp.getCalendarById(calendarId);
var startRow = 2; // First row from which data should process > 2 exempts my header row
var numRows = sheet.getLastRow(); // Number of rows to process
var numColumns = sheet.getLastColumn();
var dataRange = sheet.getRange(startRow, 1, numRows - 1, numColumns);
var data = dataRange.getValues();
var done = "Done"; // It seems that this is not used.
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var name = row[1]; //Name of Guest
var place = row[4]; //Add2
var room = row[9]; //Room Number
var inDate = new Date(row[10]); //Check-In Date
var outDate = new Date(row[11]); //Check-Out Date
var check1 = row[23]; //Booked/Blocked/Cancelled
var check2 = row[24]; //Event created and EventID (iCalUID) populated
// I modified below script.
if (check1 != "Cancelled" && check2 == "") {
var currentCell = sheet.getRange(startRow + i, numColumns);
var event = calendar.createEvent(room, inDate, outDate, {
description: 'Booked by: ' + name + ' / ' + place + '\nFrom: ' + inDate + '\nTo: ' + outDate
});
var eventId = event.getId();
currentCell.setValue(eventId);
} else if (check1 == "Cancelled" && check2 != "") {
var status = Calendar.Events.get(calendarId, check2.split("#")[0]).status;
if (status != "cancelled") {
calendar.getEventById(check2).deleteEvent();
}
}
}
}
Reference:
Events: get
I am using as a master tracking spreadsheet so all new details added after the calendar event import get mixed up when a new event is added to the sheet. HELP. I need to keep the events and new columns of data in the same row. I have 14 rows of data.
function importCalendar(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Calendar Import'); //Target sheet for events
var calendarName = sheet.getRange('C2').getValue(); //name of calendar ex. calendar#gmailcom
var start = sheet.getRange('C3').getValue(); //to date
var end = sheet.getRange('C4').getValue(); //from date
var calendar = CalendarApp.getCalendarById(calendarName);
if(calendar) {var calendarId = calendar.getId();}
if(!calendar) {
var calendar = CalendarApp.getCalendarsByName(calendarName)[0];
var calendarId = calendarName;
}
var events = calendar.getEvents(start, end);
var eventDetails = [];
for(var i = 0; i<events.length; i++){
eventDetails.push([events[i].getLocation(), events[i].getTitle(), events[i].getStartTime(),
events[i].getDescription()]);
}
//write calendar details to spreadsheet and where my problem is//
var startRow = 8;
var startCol = 2;
for(var j = 0; j<eventDetails.length; j++){
var tempRange = sheet.getRange(startRow+j, startCol, 1, eventDetails[j].length);
var eventArray = Array(eventDetails[j]);
tempRange.setValues(eventArray);
}
return eventDetails;
}
I have a to-do list app in google sheets. I have functions for filtering by "note type" and "done status" that can be in use at any given moment by the user.
I also have functions to easily add a new note of any given type. However, when running the function to add a new note, and the sheet is already filtered, I'm getting the following error:
"This operation is not supported on a range with a filtered out row."
Any advice on how I can add a row to a filtered range?
Here is the code that I am using to add a new note of a particular type:
function addNewCueNote() {
if( sheet.getSheetName() == sheetName ) {
var noteType = "CUE"
//ADDS ROW AND COPIES FORMULA DOWN
//SETS VARIABLES FOR LAST ROW AND LAST COLUMN
var lRow = sheet.getLastRow();
var lCol = sheet.getLastColumn();
//INSERT LAST ROW
sheet.insertRowsAfter(lRow, 1);
//COPY FORMULAS DOWN FOR SPECIFIED COLUMNS
sheet.getRange(lRow,firstCopyCol,1,numColCopy).copyTo(sheet.getRange(lRow+1,firstCopyCol,1,numColCopy));
//SETS NOTE TYPE
sheet.getRange(sheet.getLastRow(),noteTypeCol).setValue(noteType);
}
Grab the existing filter, remove it from the sheet, add the new row, then recreate the filter using the criteria from the initial filter.
function addNewCueNote() {
var sheet = SpreadsheetApp.getActiveSheet(); // added to get code to run; not sure if you handle elsewhere
if (sheet.getSheetName() === sheetName) {
// Save state of existing filter before removing it
var oldCriteria = [];
var filter = sheet.getFilter();
if (filter != null) {
var oldNumColumns = filter.getRange().getNumColumns();
for (var c = 1; c <= oldNumColumns; c++) {
var criteria = filter.getColumnFilterCriteria(c);
if (criteria != null) {
oldCriteria.push([c, criteria.copy()]);
}
}
filter.remove();
}
//*** PUT YOUR ROW INSERT LOGIC HERE ***
// Recreate filter on new data range
var dataRange = sheet.getDataRange();
var newFilter = dataRange.createFilter();
if (filter != null) {
var newNumColumns = dataRange.getNumColumns();
for (var i = 0; i < oldCriteria.length && oldCriteria[i][0] <= newNumColumns; i++) {
newFilter.setColumnFilterCriteria(oldCriteria[i][0], oldCriteria[i][1]);
}
}
}
#Nick there is something wrong with your code logic. In any ways this is a working code
// *** I have to add this for tests ***
var firstCopyCol = 3;
var numColCopy = 2;
var noteTypeCol = 2;
var sheet = SpreadsheetApp.getActiveSheet();
var sheetName = 'MatchImport';
// ************************************
function addNewCueNote() {
if (sheet.getSheetName() === sheetName) {
var filter = sheet.getFilter();
if (filter) {
var dataRange = sheet.getDataRange();
var oldNumColumns = filter.getRange().getNumColumns();
var newNumColumns = dataRange.getNumColumns();
var criterias = {};
for (var c = 1; c <= oldNumColumns && c <= newNumColumns; c++) {
var criteria = filter.getColumnFilterCriteria(c);
if (criteria) criterias['_' + c] = criteria;
}
filter.remove();
}
// START OF YOUR INSERT LOGIC
var noteType = 'CUE';
// ADDS ROW AND COPIES FORMULA DOWN
// SETS VARIABLES FOR LAST ROW AND LAST COLUMN
var lRow = sheet.getLastRow();
var lCol = sheet.getLastColumn(); // This is never used
// INSERT LAST ROW
sheet.insertRowsAfter(lRow, 1);
// COPY FORMULAS DOWN FOR SPECIFIED COLUMNS
sheet
.getRange(lRow, firstCopyCol, 1, numColCopy)
.copyTo(sheet.getRange(lRow + 1, firstCopyCol, 1, numColCopy));
// SETS NOTE TYPE
sheet.getRange(sheet.getLastRow(), noteTypeCol).setValue(noteType);
//* * END OF YOUR INSERT LOGIC
if (!filter) return;
dataRange = sheet.getDataRange();
var newFilter = dataRange.createFilter();
newNumColumns = dataRange.getNumColumns();
for (c = 1; c <= oldNumColumns && c <= newNumColumns; c++) {
if (criterias['_' + c])
newFilter.setColumnFilterCriteria(c, criterias['_' + c]);
}
}
}
I'm currently trying to get this piece of code to send events from a google sheet to a google calendar (Credit to Adam McFarland on this post).
My sheet is currently around 300 rows & growing so to speed things up I've set the range to start at row 248. But this then seems to throw off the part that notes the event as 'done'. It sets value of "In 2 calendar" to rows 2, 3, 4 & 5?!?
Easy solution would be just to set the range to the whole sheet again but I'm still learning. I'd like to learn what exactly here isn't working correctly, and also a bit more about how iteration works.
//mark as entered, enter ID
sheet.getRange(i+2, 32).setValue('In 2 calendar');
Complete code below:
function pushToCalendar() {
//spreadsheet variables
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var range = sheet.getRange(248,1,lastRow,40);
var values = range.getValues();
var updateRange = sheet.getRange('G1');
var numValues = 0;
for (var i = 0; i < values.length; i++) {
//check to see if name and type are filled out - date is left off because length is "undefined"
if ((values[i][0].length > 0) && (values[i][2].length > 0)) {
//check if it's been entered before
if (values[i][30] != 'In calendar') {
//Declare which calendar ID to use (IGNORE THIS FOR NOW)
var calendar = CalendarApp.getCalendarById('calendarID')
// if (values [i][3] != 'Tropical 2450 Pontoon'){
// var calendar = CalendarApp.getCalendarById('calendarID')
//create event https://developers.google.com/apps-script/class_calendarapp#createEvent
var newEventTitle = values[i][3]+'. '+values[i][2]+'. '+values[i][13]
+'. '+values[i][5]+'/'+values[i][6]+'/'+values[i][7]
+'. '+values[i][18]+' total, '+values[i][25]+' to pay. '+values[i][0];
// var newEvent = calendar.createEvent('hello', Date[i][1], Date[i][5]);
var newEvent = calendar.createEvent(newEventTitle,
//new Date(values[i][6]),
new Date(values[i][32]),
new Date(values[i][33]));
//{guests:'tures.com.au', sendInvites: true});
//mark as entered, enter ID
sheet.getRange(i+2, 32).setValue('In 2 calendar');
} //could edit here with an else statement
}
numValues++;
}
}
My spreadsheet is composed of a main sheet that is populated using a form plus several other sheets for the people who work with the responses submitted through the form. A script delegates the form responses to these other sheets depending on the type of item described in the response.
The problem is, when Person A deletes an item from their respective sheet, it doesn't delete in the main sheet.
My idea is that when you type a set password into the corresponding cell in row 'Q' in Person A's sheet, it matches the item by timestamp to the original form submission and deletes both the version of the item in Person A's sheet as well as the main sheet. However, I can't figure out what to set the range to to get it to point to the row in the array. Everything I have tried has sent back "undefined" in the debugger and won't delete anything. I think the problem is that I don't know how to get the row from the array that I have made. See my code below:
function onEdit() {//copies edited items from individual selector sheets back onto main spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var actSheet = ss.getActiveSheet();
var responseSheet = ss.getSheetByName("Item Request");
var actCell = actSheet.getActiveCell();
var actRow = actCell.getRow();
var actVal = actCell.getValue();
var actLoc = actCell.getA1Notation();
var last = actSheet.getLastRow();
var respLast = responseSheet.getLastRow();
var dataA = responseSheet.getRange(1, 1, respLast, 1).getValues(); //compiles an array of data found in column A through last row in response sheet
var tstamp1 = actSheet.getRange(actCell.getRow(), 1);
var tsVal1 = tstamp1.getValue();
var colEdit = actCell.getColumn();
//===========THIS IS WHERE I'M STUCK=======================
if ((actVal == "p#ssword") && (colEdit == 17)) {
for (i = 1; i < dataA.length; i++) {
if (dataA[i][0].toString == tsVal1.toString()) {
responseSheet.deleteRow(i + 1);
actSheet.deleteRow(actRow);
break;
}
}
}
else if (colEdit == 15) { //checks the array to see if the edit was made to the "O" column
for (i = 1; i < dataA.length; i++) {//checking for timestamp match and copies entry
if (dataA[i][0].toString() == tsVal1.toString()) {
var toEdit = responseSheet.getRange(i + 1, 16);
toEdit.setValue(actVal);
}
}
}
else if (colEdit == 16) { // checks the array to see if the edit was made in the "P" column
for (i = 1; i < dataA.length; i++) {//checking for timestamp match and copies entry
if (dataA[i][0].toString() == tsVal1.toString()) {
var toEdit = responseSheet.getRange(i + 1, 17);
toEdit.setValue(actVal);
}
}
}
else {return;}
}//end onEdit
I don't believe these are proper commands delRow.deleteRow();actCell.deleteRow(); Take a look at the documentation;
Okay I rewrote that function for you a bit but I'm stilling wondering about a couple of lines.
function onEdit(e)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var actSheet = ss.getActiveSheet();
var responseSheet = ss.getSheetByName("Item Request");
var actCell = actSheet.getActiveCell();
var actRow = actCell.getRow();
var actVal = actCell.getValue();
var colEdit = actCell.getColumn();
var respLast = responseSheet.getLastRow();
var dataA = responseSheet.getRange(1, 1, respLast, 1).getValues();
var tstamp1 = actSheet.getRange(actRow, 1);
var tsVal1 = tstamp1.getValue();
for(var i=0;i<dataA.length;i++)
{
if(new Date(dataA[i][0]).valueOf()==new Date(tsVal1).valueOf())
{
if (actVal=="p#ssword" && colEdit==17)
{
responseSheet.deleteRow(i + 1);
actSheet.deleteRow(actRow);
}
else if(colEdit==15)
{
var toEdit = responseSheet.getRange(i + 1, 16);//?
toEdit.setValue(actVal);//?
}
else if (colEdit == 16)
{
var toEdit = responseSheet.getRange(i + 1, 17);//?
toEdit.setValue(actVal);//?
}
}
}
}
Can you explain the function of the lines with question marked comments?