Apps Script getEventById() returns null - google-apps-script

I am new to Apps Script and struggling with the "getEventById()" function.
My goal is to delete an event entry on Google Calendar via Google Sheets when you press a button.
I already managed to get the event id via Apps Script and it´s Google API V3, but when I hand it over to "getEventById" as parameter, it returns null, even when I "hardcode" the id.
Here´s my code. I removed some parts since those aren´t important I think:
function calDate(){
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
var calId = spreadsheet.getRange("N1").getValue();
var calEvent = CalendarApp.getCalendarById(calId);
var ui = SpreadsheetApp.getUi();
var selection = spreadsheet.getSelection();
var selectedRow = selection.getActiveRange().getA1Notation();
var rowRange = sheet.getRange(selectedRow);
var rowNumber = rowRange.getRow();
var colRange = sheet.getRange(selectedRow);
var colNumber = colRange.getColumn();
if (colNumber !== 15){
//wait for showtime
}
else{
// its showtime
var combinedRange = "O" + rowNumber;
var sheetData = sheet.getRange(rowNumber, 3, 1, 15).getValues();
if(sheetData[0][12] == false){
var dateStart = new Date(sheetData[0][7]);
var dateEnd = new Date(sheetData[0][8]);
var KdName = sheetData[0][0];
var BV = event_id[0][4];
var combinedNames = KdName + " - " + BV;
var items = Calendar.Events.list(calId, {
timeMin: dateStart.toISOString(),
timeMax: dateEnd.toISOString(),
q: combinedNames
}).items;
}
else{
var testVar = calEvent.getEventById(/*This is where I would put the htmlLink (the event-id)*/);
console.log(testVar);
}
}
}
Hopefully those informations are enough and if not, feel free to ask for more.
I really hope you guys can help me out!
Kind regards
EDIT & SOLUTION
Okay guys, thanks to Mateo Randwolf, who kindly opened an issue at Google about this, I was able to figure it out. This is the link with an example how to get the the ID from the event and hand that id over to the "getEventById()" function. Or here as a code-block:
function findEventID() {
var now = new Date();
var nextEvent = new Date(now.getTime() + (2 * 60 * 60 * 1000));
var event = CalendarApp.getDefaultCalendar().getEvents(now, nextEvent);
ID = event[0].getId();
Logger.log('EventID: ' + event[0].getId());
Logger.log(CalendarApp.getDefaultCalendar().getEventById(ID));
}
Now it gets funny. This line:
Logger.log('EventID: ' + event[0].getId());
returns the event-id like it should.
But this one:
Logger.log(CalendarApp.getDefaultCalendar().getEventById(ID));
doesn´t show anything except "{}", which is weird.
But if you apply "deleteEvent()" on it, like so:
calEvent.getEventById(ID).deleteEvent(); //calEvent is a CalendarApp, see above
It actually deletes the event from the calendar!
With that, I´d say we found the solution or at least a bypass.

Issue
Hi ! So it seems to me that getEventById() has a bug that returns null instead of the event object as I was getting the exact same behaviour you were reporting in this question. I have filed this behaviour to the public issue tracker, you can find it here with all the discussion on this behaviour.
I hope this has helped you. Let me know if you need anything else or if you did not understood something. :)

Using the Calendar API search query to find events in a calendar
function calDate(){
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sh=ss.getActiveSheet();
var calId=ss.getRange("N1").getValue();
var calEvent=CalendarApp.getCalendarById(calId);
var row=ss.getActiveRange().getRow();
var col=ss.getActiveRange().getColumn()
if (col!=15){
//wait for showtime
}else{
var vs=sh.getRange(row, 3, 1, 15).getValues();
if(vs[0][12] == false){
var dateStart=new Date(vs[0][7]);//col J
var dateEnd=new Date(vs[0][8]);//col K
var KdName=vs[0][0];//col 3
event_id below is not defined
var BV=event_id[0][4];//col G
var combinedNames=KdName + " - " + BV;
var items=Calendar.Events.list(calId, {timeMin: dateStart.toISOString(),timeMax: dateEnd.toISOString(),q: combinedNames}).items;
}
else{
var testVar=calEvent.getEventById(/*This is where I would put the htmlLink (the event-id)*/);
console.log(testVar);
}
}
}
Since you couldn't share your spreadsheet I share mine with an example
One thing that helps a lot is playing with the API explorer to figure what works and what doesn't. If you want to display all of the fields you can use * and this example proved very helpful as well
Here's the code:
function myOwnEventSearch() {
var calendarId='***********calendar id**************';
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet235');
var sr=2;
var sc=2
var rg=sh.getRange(sr,sc,sh.getLastRow()-sr+1,sh.getLastColumn()-sc+1);
var vA=rg.getValues();
var hA=sh.getRange(sr-1,sc,1,sh.getLastColumn()-sc+1).getValues()[0];
var idx={};//locates the data index from column header names
hA.forEach(function(h,i){idx[h]=i;});
var cal=CalendarApp.getCalendarById(calendarId);
var html='<style>td,th{}</style><table><tr><th>Summary</th><th>Start</th><th>End</th><th>Id</th></tr>'
for(var i=0;i<vA.length;i++) {
if(!vA[i][idx['Id']] && vA[i][idx['DateFrom']] && vA[i][idx['DateTo']] && vA[i][idx['SearchString']]) {
var request={timeMin:new Date(vA[i][idx["DateFrom"]]).toISOString(),timeMax:new Date(vA[i][idx["DateTo"]]).toISOString(),q:vA[i][idx["SearchString"]],showDeleted: false,singleEvents:true,maxResults:10,orderBy:"startTime"};
var resp=Calendar.Events.list(calendarId, request);
if(resp.items.length>0) {
var idA=[];
resp.items.forEach(function(item,j){
html+=Utilities.formatString('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>', item.summary,item.start,item.end,item.id);
idA.push(item.id);
});
sh.getRange(i+sr,idx['Id']+sc).setValue(idA.join(', '))
}
}
}
html+='<table>';
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html).setWidth(800), "My Events")
}
Here's the spreadsheet before the script runs.
Here's the dialog that displays the search results.
Here's what the spreadsheet looks like after running script:
The Event Ids were copied into the Id Column
And these were the four events I created on my calendar::

Here is how I worked around this. I stored all the events (from the range I was interested in) in a JavaScript Map() so I can find them later:
var cal = CalendarApp.getCalendarById("/* supply your calendar ID here*/");
if (!cal) {
Logger.log("Calendar not found for ID:" + calendarID);
} else {
var calEvents = cal.getEvents(new Date("March 8, 2010"), new Date("March 14, 2025"));
// Store them by eventId for easy access later
var calEventMap = new Map();
for (var j in calEvents) {
calEventMap.set(calEvents[j].getId(), calEvents[j]);
}
/* Later when you need to look up by iCalID... */
var calEvent = calEventMap.get(eventID);
}

Works for me when you get the calendar by id like this:
const calendar = CalendarApp.getCalendarById("theCalendarId");
const event = calendar.getEventById("someEventId");
Now the event is not null, but the actual event, and you can do whatever you want with it from here!

Related

Google Scripts - View Log stuck at "Waiting for logs, please wait..."

I'm trying to run a script for my Google Sheet on Scripts, and a function isn't working properly. I have some loggers in place to check why this is happening, but anytime I try to open the Logs tab, I get this:
... and it's just stuck there forever.
Has anyone ever had this problem? Any potential fixes? Thanks
EDIT: My executions window looks like so:
EDIT 2: Here is the code I'm trying to run, with segment = 1. SPREADSHEETS is just a variable that I'm unfortunately not able to share, but it just contains some import segment information that directs to either 1 or 2.
function CopyPasteAllSheets(segment) {
for (x in SPREADSHEETS) {
if (SPREADSHEETS[x].IMPORTSEGMENT != segment) {
// DRR added app which is redundant to intakeSpreadhseet, but keeps logic more readable
app.toast('running loop')
console.log("ID: " + SPREADSHEETS[x].SOURCE.ID + "NO MATCH");
} else {
// Logger.log("x: "+ x) // keep commented out
var intakeSpreadsheet = SpreadsheetApp.openById(SPREADSHEETS[x].INTAKE.ID);
var intakeSheet = intakeSpreadsheet.getSheetByName(SPREADSHEETS[x].INTAKE.SHEET); //confirm formatting conventions
// This is functionally equivlent to the above, except we don't have a reference to intakeSpreadsheet anymore
// Access the Spreadsheet and sheet you want to copy the data TO
console.log("ID: "+ SPREADSHEETS[x].SOURCE.ID)
var sourceSpreadsheet = SpreadsheetApp.openById(SPREADSHEETS[x].SOURCE.ID);
var sourceSheet = sourceSpreadsheet.getSheetByName(SPREADSHEETS[x].SOURCE.SHEET);
var sourceStartRow = SPREADSHEETS[x].SOURCE.STARTROW;
var sourceStartCol = SPREADSHEETS[x].SOURCE.STARTCOL;
var sourceRangeCol = SPREADSHEETS[x].SOURCE.ENDCOL - SPREADSHEETS[x].SOURCE.STARTCOL + 1;
// Get the range of the data you want and the range where you want the data to go
var rowsToCopy = sourceSheet.getLastRow()-sourceStartRow+1; // is +1 too conservative, check...
var rangeToCopy = sourceSheet.getRange(sourceStartRow,sourceStartCol,rowsToCopy, sourceRangeCol);
var dataToCopy = rangeToCopy.getValues();
var numRows = rowsToCopy;
var numColumns = sourceRangeCol;
var intakeStartRow = SPREADSHEETS[x].INTAKE.STARTROW;
var intakeStartCol = SPREADSHEETS[x].INTAKE.STARTCOL;
var rangeToPaste = intakeSheet.getRange(intakeStartRow,intakeStartCol, numRows,numColumns); // WAS FORMERLY 1,20, ..,.. ~DRR 7/14
rangeToPaste.setValues(dataToCopy);
}
}
}

Google Script Error - Cannot find method add

I have been puzzled by this error. I have not been able to figure out what I am missing. Without the calls to headPanel the UI was working but would not insert the title or text label so I added the Horizontal panel in hopes of adding the text, but alas error. Please help me with this problem and I need to know what I am messing up.
function doGet(e){
var app = UiApp.createApplication();
var headPanel = app.createHorizontalPanel().setId('headPanel');
var header = app.setTitle('Detention Attendance');
var label = app.createLabel("Please enter attendance for Detention by clicking the checkbox next to the student's name if they were present. Then click Sumbit.");
headPanel.add(header); //* ERROR OCCURS HERE
headPanel.add(label);
app.add(pane);
var panel = app.createVerticalPanel();
var flexTable = app.createFlexTable().setStyleAttribute('border', '2px solid black')
.setStyleAttribute('borderCollapse','collapse')
.setBorderWidth(2);
//Get Data from spreadsheet
var spreadsheetId = 'xxxxxxxxxxxxxxxxxxxxxxxx';//Change it to your Spreadsheet ID
var dataArray = getData(spreadsheetId);
//Load data into table cells
Logger.log(dataArray);
for (var row = 0; row<dataArray.length; row++){
if (row > 0) {
var ticketDate = dataArray[row] [0];
var dateStamp = Utilities.formatDate(ticketDate, "America/Chicago", "MM/dd/yyyy");
Logger.log("dateStamp = " +dateStamp);
dataArray[row] [0] = dateStamp;
var ticketDate2 = dataArray[row] [16];
var dateStamp2 = Utilities.formatDate(ticketDate2, "America/Chicago", "MM/dd/yyyy");
dataArray[row] [16] = dateStamp2;
Logger.log("DateStamp = " +dateStamp2);
}
flexTable.setStyleAttribute("color", "purple").setText(row, 2, dataArray[row][2].toString()).setWidth('300px');
flexTable.setText(row, 0, dataArray[row][0].toString()).setWidth('600px');
flexTable.setText(row, 4, dataArray[row][16].toString()).setWidth('300px');
}
panel.add(flexTable);
app.add(panel);
return app;
}
function getData(spreadsheetId){
var ss = SpreadsheetApp.openById(spreadsheetId);
var sheet = ss.getSheets()[0].getDataRange();
Logger.log(sheet);
return sheet.getValues();
}
Your header var is a UiInstance - in fact it's the same instance as app, returned as a convenience for chaining by the call to .setTitle().
The .add() method is looking for a Widget as a parameter, and not finding it.
You don't need to keep that header var - it's only causing you trouble. In fact, you could set the Title as you create the UiApp instance:
var app = UiApp.createApplication().setTitle('Detention Attendance');

Instant search using TextBox and onKeyUp and onKeyDown

I would like to make Google Apps Script webapp that search spreadsheet instantly as you type in the TextBox.
The implementation I"m using now is following. For each key up event new search is lauched with the current search string. I'm using delay on the function that onKeyUp is calling and if the onKeyDown is called I try to kill the previous function call and just go with the new. This works sometimes but the search takes different time to complete so the last one to finish is not allways the right one.
I don't know how to solve this task in Google Apps script. This can be implemented using something like this but these functions are not awailable:
var timer;
function up(){
setTimeout(mySpreadsheetSearchFunction, 500);
}
function down(){
clearTimeout (timer);
}
This is the code of the current implementation that is not working correctly:
function up(){
var cache = CacheService.getPrivateCache();
var now = parseInt(cache.get('iterate'));
Utilities.sleep(500);
if(parseInt(cache.get('iterate')) !== parseInt(now)){
return;
}else{
search();
}
showInGui();
}
function down(){
var cache = CacheService.getPrivateCache();
cache.put('iterate', 1+parseInt(cache.get('iterate')));
}
The CacheService is probably wrong tool for this job, what could be beter? Is this the right way to implement something like this?
Not sure you have to use the cache feature to get a fast result... I made such a script that works pretty well, I use variants of it all the time ;-)
you can test it on this test sheet
here is how it works, the script is maybe quite long but look only at the handler part (click)
On other version I read the spreadsheet outside of the function so the array of data becomes a global variable and mostly to limit the number of spreadsheet calls (it can hit the quota limit in this version sometimes)
// G. Variables
var sh = SpreadsheetApp.getActiveSheet();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var lastrow = ss.getLastRow();
// ...
function onOpen() {
var menuEntries = [ {name: "Search GUI", functionName: "searchUI"},
];
ss.addMenu("Search Utilities",menuEntries);// custom menu
}
// Build a simple UI to enter search item and show results + activate result's row
function searchUI() {
var app = UiApp.createApplication().setHeight(130).setWidth(400);
app.setTitle("Search by name / lastname / adress");
var panel = app.createVerticalPanel();
var txtBox = app.createTextBox().setFocus(true);
var label=app.createLabel(" Item to search for :")
panel.add(label);
txtBox.setId("item").setName("item");
var label0=app.createLabel("Row").setWidth("40");
var label1=app.createLabel("Name").setWidth("120");
var label2=app.createLabel("Lastname").setWidth("120");
var label3=app.createLabel("Street").setWidth("120");
var hpanel = app.createHorizontalPanel();
hpanel.add(label0).add(label1).add(label2).add(label3)
//
var txt0=app.createTextBox().setId("lab0").setName("0").setWidth("40");
var txt1=app.createTextBox().setId("lab1").setName("txt1").setWidth("120");
var txt2=app.createTextBox().setId("lab2").setName("txt2").setWidth("120");
var txt3=app.createTextBox().setId("lab3").setName("txt3").setWidth("120");
var hpanel2 = app.createHorizontalPanel();
hpanel2.add(txt0).add(txt1).add(txt2).add(txt3)
var hidden = app.createHidden().setName("hidden").setId("hidden");
var subbtn = app.createButton("next ?").setId("next").setWidth("250");
panel.add(txtBox);
panel.add(subbtn);
panel.add(hidden);
panel.add(hpanel);
panel.add(hpanel2);
var keyHandler = app.createServerHandler("click");
txtBox.addKeyUpHandler(keyHandler)
keyHandler.addCallbackElement(panel);
//
var submitHandler = app.createServerHandler("next");
subbtn.addClickHandler(submitHandler);
submitHandler.addCallbackElement(panel);
//
app.add(panel);
ss.show(app);
}
//
function click(e){
var row=ss.getActiveRange().getRowIndex();
var app = UiApp.getActiveApplication();
var txtBox = app.getElementById("item");
var subbtn = app.getElementById("next").setText("next ?")
var txt0=app.getElementById("lab0").setText('--');
var txt1=app.getElementById("lab1").setText('no match').setStyleAttribute("background", "white");// default value to start with
var txt2=app.getElementById("lab2").setText('');
var txt3=app.getElementById("lab3").setText('');
var item=e.parameter.item.toLowerCase(); // item to search for
var hidden=app.getElementById("hidden")
var data = sh.getRange(2,2,lastrow,3).getValues();// get the 3 columns of data
for(nn=0;nn<data.length;++nn){ ;// iterate trough
Logger.log(data[nn])
if(data[nn].toString().toLowerCase().match(item.toString())==item.toString()&&item!=''){;// if a match is found in one of the 3 fields, break the loop and show results
txt0.setText(nn+2);
txt1.setText(data[nn][0]).setStyleAttribute("background", "cyan");
txt2.setText(data[nn][1]);
txt3.setText(data[nn][2]);
sh.getRange(nn+2,2).activate();
subbtn.setText("found '"+item+"' in row "+Number(nn+2)+", next ?");
hidden.setValue(nn.toString())
break
}
}
return app ;// update UI
}
function next(e){
var row=ss.getActiveRange().getRowIndex();
var app = UiApp.getActiveApplication();
var txtBox = app.getElementById("item");
var subbtn = app.getElementById("next").setText("no other match")
var hidden=app.getElementById("hidden");
var start=Number(e.parameter.hidden)+1;//returns the last search index stored in the UI
var item=e.parameter.item.toLowerCase(); // item to search for
var txt0=app.getElementById("lab0");
var txt1=app.getElementById("lab1").setStyleAttribute("background", "yellow");
var txt2=app.getElementById("lab2");
var txt3=app.getElementById("lab3");
var data = sh.getRange(2,2,lastrow,3).getValues();// get the 3 columns of data
for(nn=start;nn<data.length;++nn){ ;// iterate trough
if(data[nn].toString().toLowerCase().match(item.toString())==item.toString()&&item!=''){;// if a match is found in one of the 3 fields, break the loop and show results
txt0.setText(nn+2);
txt1.setText(data[nn][0]).setStyleAttribute("background", "cyan");
txt2.setText(data[nn][1]);
txt3.setText(data[nn][2]);
sh.getRange(nn+2,2).activate();
subbtn.setText("found '"+item+"' in row "+Number(nn+2)+", next ?");
hidden.setValue(nn.toString())
break
}
}
return app ;// update UI
}
Using the HtmlService you can instead implement this in pure HTML and JavaScript. This gives you the ability to load the spreadsheet data once, and then do the search client-side, where the performance should be better.

Parsing XML Data that I receive from UrlFetch

I want to parse the data I get from UrlFetch into a spreadsheet, but all I'm getting is undefined can someone show me what i'm doing wrong
The xml is at the address https://dl.dropbox.com/u/11787731/Minecraft/bans.xml
function runevery15mins() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("MC Bans");
sheet.clearContents();
var banURL = "https://dl.dropbox.com/u/11787731/Minecraft/bans.xml";
var banXML = UrlFetchApp.fetch(banURL).getContentText();
var banDOC = Xml.parse(banXML, false);
var mcuser = banDOC.bans;
var x = 0;
for(var c=0; c>mcuser.length;c++){
var name = mcuser.getElement("username")[c].getText();
var date = mcuser.getElement("date")[c].getText();
var reason = mcuser.getElement("reason")[c].getText();
var duration = mcuser.getElement("duration")[c].getText();
}
sheet.appendRow([name, date, reason, duration]);
}
You have some small errors in your code.
For example, the second argument in the for loop needs to be c<mcuser.length.
Using the Xml service documentation, this worked for me
function runevery15mins() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("MC Bans");
sheet.clearContents();
var banURL = "https://dl.dropbox.com/u/11787731/Minecraft/bans.xml";
var banXML = UrlFetchApp.fetch(banURL).getContentText();
var banDOC = Xml.parse(banXML, false);
// Get all the child nodes from the document element that are 'user' nodes
var mcusers = banDOC.getElement().getElements('user');
for(var c=0; c<mcusers.length;c++){
var user = mcusers[c];
var name = user.getElement('username').getText();
var date = user.getElement('date').getText();
var reason = user.getElement('reason').getText();
var duration = user.getElement('duration').getText();
sheet.appendRow([name, date, reason, duration]);
}
}
Note for example that the sheet.appendRow line is INSIDE the loop, not outside as you had it before. I also deleted the X variable, since I didn't see any purpose for it.
I also created a user variable, which is an XmlElement, to make it easier to understand how to get the different contents of each node.
You were almost there.
Looks like there was another array you needed to drill down into. Also, your call back to the spreadsheet should be in the loop. Try this:
...
var mcuser = banDOC.bans.user;
for(var i in mcuser){
var name = mcuser[i].getElement("username").getText();
var date = mcuser[i].getElement("date").getText();
var reason = mcuser[i].getElement("reason").getText();
var duration = mcuser[i].getElement("duration").getText();
sheet.appendRow([name, date, reason, duration])
}

Error occured: TypeError: Cannot call method "createEvent" of undefined via Google Script

Within the second half of my script and function, I keep getting this Cannot call method error, and I do not know why. I mimic'd the template script exactly, but I am unsure why it does not call the method.
Any insight would be greatly appreciated.
Thanks.
function createEvents(e){
//Get the active application
var app = UiApp.getActiveApplication();
try{
//get the entries;
var eventDate = e.parameter.eventDate;
var eventPeople = e.parameter.eventPeople;
var eventCompany = e.parameter.eventCompany;
var eventName = e.parameter.eventName;
var eventTime = e.parameter.eventTime;
var eventPhone = e.parameter.eventPhone;
var eventEmail = e.parameter.eventEmail;
var eventTaken = e.parameter.eventTaken;
//Get the calendar
var cal = CalendarApp.getCalendarsByName('Phoenix Reservations')[0];//Change the calendar name
var eventStartTime = eventDate;
//End time is calculated by adding an hour in the event start time
var eventEndTime = new Date(eventDate.valueOf()+60*60*1000);
//Create the events
cal.createEvent(eventPeople,eventCompany,eventName,eventTime,eventPhone,eventEmail,eventTaken);
//Log the entries in a spreadsheet
var ss = SpreadsheetApp.openById('KEY_TAKEN_OUT');//Change the spreadhseet key to yours
var sheet = ss.getSheets()[0];
sheet.getRange(sheet.getLastRow()+1, 1, 1, 5).setValues([[new Date(), eventDate,eventPeople,eventCompany,eventName,eventTime,eventPhone,eventEmail,eventTaken, 'Event created']]);
//Show the confirmation message
app.add(app.createLabel('Event created Successfully'));
//make the form panel invisible
app.getElementById('panel').setVisible(false);
return app;
}
//If an error occurs, show it on the panel
catch(e){
app.add(app.createLabel('Error occured: '+e));
return app;
}
}
This line probably returns nothing :
var cal = CalendarApp.getCalendarsByName('Phoenix Reservations')[0];//Change the calendar name
You could log it to confirm like this Logger.log(cal)
Is 'Phoenix Reservations' a calendar name that you own or that you have write access to ?
EDIT : could you test this simple function to see if everything is ok with this calendar?
It will create an event just now.
function testcal(){
var cal = CalendarApp.openByName('Phoenix Reservations');// or you can replace this with your var definition : same result normally ;-)
if (cal) {
var title = 'Test Event';
var start = new Date();
var end = new Date(start.valueOf()+60*60*1000);
Logger.log(cal.getName()+' '+start+' '+end)
var desc = 'Created using Google Script';
var loc = 'there';
var event = cal.createEvent(title, start, end, {
description : desc,
location : loc
});
}
}
EDIT 2 : I modified the logger to make it show the calendar name.