I am coding a room booking system using combination of Google forms and Google calendar.
When there is a new booking order:
An event will be automatically created on the selected calendar.
An edit response URL will also be generated automatically and put in column 10 of the spreadsheet in the same row where the form answer was inserted.
// This is the function to generate the edit URL (which works perfectly).
function getEditUrl(request) {
var formRes = FormApp.openById('XXXXXXXXXXXXXXXXXXXXXXXXXXXX');
var sheetRes = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('YYYYYYYYYY');
var data = sheetRes.getDataRange().getValues();
var urlCol = 10;
var responses = formRes.getResponses();
var timestamps = [],
urls = [],
resultUrls = [];
for (var i = 0; i < responses.length; i++) {
timestamps.push(responses[i].getTimestamp().setMilliseconds(0));
urls.push(responses[i].getEditResponseUrl());
}
for (var j = 1; j < data.length; j++) {
resultUrls.push([data[j][0] ? urls[timestamps.indexOf(data[j][0].setMilliseconds(0))] : '']);
}
sheetRes.getRange(2, urlCol, resultUrls.length).setValues(resultUrls);
};
However, problem occurs when there are more than 2 orders; as the next order will delete the calendar event from the previous order.
// This is the function to update the calendar event.
function updateCalendar(request) {
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var range = sheet.getRange(2, 1, lastRow, 13);
var values = range.getDisplayValues();
var calendar = CalendarApp.getCalendarById('XXXXXXXXXXXXXXXXXXXXXXXX#group.calendar.google.com');
for (var i = 0; i < responses.length; i++) {
getConflicts(request);
if (request.eventConflict == "conflict") {
sheet.getRange(lastRow, 11).setValue("conflict");
break;
} else if (request.eventConflict == "approve") {
var newEvent = calendar.createEvent("booked", request.date, request.endTime);
var newEventId = newEvent.getId().split('#')[0];
sheet.getRange(lastRow, 11).setValue("approve");
sheet.getRange(lastRow, 12).setValue(newEventId);
break;
}
}
for (var j = 1; j < values.length; j++) {
if (values[j][10] == "approve") {
var eventEditId = calendar.getEventSeriesById(values[j][11]);
eventEditId.deleteEventSeries();
sheet.getRange(j + 2, 11).setValue("");
getConflicts(request);
if (request.eventConflict == "approve" && values[j][10].length > 1) {
var newEvent = calendar.createEvent("booked", request.date, request.endTime);
var newEventId = newEvent.getId().split('#')[0];
sheet.getRange(j + 2, 11).setValue("approve");
sheet.getRange(j + 2, 12).setValue(newEventId);
break;
} else {
sheet.getRange(j + 2, 11).setValue("conflict");
break;
}
}
}
};
My questions:
How to make sure that when respondent edits his/her own response, it will always update event from the same column as the edit URL? --> I have separate function that will send edit URL to respondents
When there is more than two submission, the 3rd submission will delete event of the 2nd one. (I am sure the issue is on the updateCalendar() function).
I have been struggling so much for the past few days trying to figure out the best way to come up with best loop method. Any help / response is greatly appreciated.
EDIT:
This is the column description of the sheets (separated with |):
Timestamp
Email Address
name
Check-in date
Check-out date
Room
No. of people
total day
total
edit URL
Event Conflict
Event ID
This is the function to get event conflicts in the calendar:
function getConflicts(request){
var conflicts = request.calendar.getEvents(request.date, request.endTime);
if (conflicts.length > 0) {
request.eventConflict = "conflict";
} else {
request.eventConflict = "approve"
}
};
And this is the main function that will be triggered on formsubmit:
function main(){
var request = new Submission(lastRow);
getEndTime(request);
draftEmail(request);
updateCalendar(request);
};
This is the screenshot of the sheet
Finally I found one way to retrieve the edited row by using e.range method. So basically I created another sheet inside the same spreadsheet. When there is a new submission, it will automatically copy the new submission to the second sheet. And when there is an edited submission, it will go through the copy sheet to find the edited row, and then edit it (as well as the calendar). Credit to Tedinoz
function updateCalendarTwo(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var responsename = "AAAAAAAAAAAAAAA"
var copyname = "BBBBBBBBBBBB";
var responsesheet = ss.getSheetByName(responsename);
var copysheet = ss.getSheetByName(copyname);
var calendar = CalendarApp.getCalendarById('CCCCCCCCCCCCCCCCCCCC');
// columns on copysheet
var checkInCol = 4;
var checkOutCol = 5;
var roomNumCol = 6;
var appCol = 11
var eventIDCol = 12;
var revCol = 14;
var response = e.range;
var rRow = response.getRow()
var rLC = responsesheet.getLastColumn();
var cLC = copysheet.getLastColumn();
var rLR = responsesheet.getLastRow();
var cLR = copysheet.getLastRow();
if (rLR > cLR){
var resprange = responsesheet.getRange(rLR,1,1,rLC);
var respdata = resprange.getValues();
copysheet.appendRow(respdata[0]);
var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
var startDate = copysheet.getRange(rRow,checkInCol).getValue();
var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
if (conflicts.length < 1) {
var event = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
var eventID = event.getId().split('#')[0];
copysheet.getRange(rRow,appCol).setValue("approve");
copysheet.getRange(rRow,eventIDCol).setValue(eventID);
} else {
copysheet.getRange(rRow,appCol).setValue("conflict");
}
} else {
var resprange = responsesheet.getRange(rRow,1,1,9);
var respdata = resprange.getValues();
var copyrespRange = copysheet.getRange(rRow,1,1,9);
copyrespRange.setValues(respdata);
var respAppRange = copysheet.getRange(rRow,appCol);
var respApp = respAppRange.getValue();
if (respApp == 'conflict') {
var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
var startDate = copysheet.getRange(rRow,checkInCol).getValue();
var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
if (conflicts.length < 1) {
var editedEvent = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
var editedEventID = editedEvent.getId().split('#')[0];;
copysheet.getRange(rRow,appCol).setValue("edited");
copysheet.getRange(rRow,eventIDCol).setValue(editedEventID);
} else {
copysheet.getRange(rRow,appCol).setValue("conflict");
};
} else {
var eventEditId = copysheet.getRange(rRow,eventIDCol).getDisplayValue();
var editedEvent = calendar.getEventSeriesById(eventEditId);
editedEvent.deleteEventSeries();
var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
var startDate = copysheet.getRange(rRow,checkInCol).getValue();
var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
if (conflicts.length < 1) {
var editedEvent = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
var editedEventID = editedEvent.getId().split('#')[0];;
copysheet.getRange(rRow,appCol).setValue("edited");
copysheet.getRange(rRow,eventIDCol).setValue(editedEventID);
} else {
copysheet.getRange(rRow,appCol).setValue("conflict");
};
};
var revRange = copysheet.getRange(rRow,revCol);
var revOldValue = revRange.getValue();
if (revOldValue == null || revOldValue == ""){
revOldValue = 0;
}
var revNewValue = revOldValue+1;
revRange.setValue(revNewValue);
}
}
I have a simple app in which users complete some basic details of a meeting in a google sheet and then export these to their google calendar. Script as follows:
function exportEvents() {
var sheet = SpreadsheetApp.getActiveSheet();
var headerRows = 1;
var range = sheet.getDataRange();
var data = range.getValues();
// var calId = sheet.getRange("H2:H2").getValue();
var calId = "xxxxxxxxxxxx";
var cal = CalendarApp.getCalendarById(calId);
for (i=0; i<data.length; i++) {
if (i < headerRows) continue;
var row = data[i];
var date = new Date(row[1]);
var title = row[2];
var initial = row[0];
var concatTitle = initial + " - " + title;
var tstart = new Date(row[3]);
tstart.setDate(date.getDate());
tstart.setMonth(date.getMonth());
tstart.setYear(date.getYear());
// var tstop = new Date(row[4]);
var tstop = new Date(tstart + row[4]);
tstop.setDate(date.getDate());
tstop.setMonth(date.getMonth());
tstop.setYear(date.getYear());
var loc = row[5];
var desc = row[6];
var id = row[7];
try {
var event = cal.getEventSeriesById(id);
}
catch (e) {
// do nothing - we just want to avoid the exception when event doesn't exist
}
if (!event) {
var newEvent = cal.createEvent(concatTitle, tstart, tstop, {description:desc,location:loc}).getId();
row[7] = newEvent;
}
else {
event.setTitle(concatTitle);
event.setDescription(desc);
event.setLocation(loc);
var recurrence = CalendarApp.newRecurrence().addDailyRule().times(1);
event.setRecurrence(recurrence, tstart, tstop);
}
debugger;
}
range.setValues(data);
}
The whole thing works great, except for when the user puts more than 60 mins as the length of the meeting in column 4. Anything up to 59 mins works great and the event is logged with the correct start and end time. However, when I enter 60 or more mins, the error reads 'Event start time must be before event end time."
I'm sure I'm doing something very simple very wrong, but any help would be greatly appreciated.
I am trying to get specific values from the general site that contains the annual reports of public-listed firms using Selenium Webdriver JS. Kindly help me investigate. I get the issue on clicking the Annual Report link.
The process that I'm trying to automate is listed below:
Go to site (http://edge.pse.com.ph/financialReports/form.do)
Filter by type of report (sendKeys('Annual Report') and fromDate attribute value to 4-23-2012 (year 2012 to get 4-5 annual reports of each firm)
click btnSearch
click Annual Report link per company row to view report (a tag is inside a td)
switch to the popup window
get specific values as object values and store it in the array
close popup window and get back to initial window
Repeat it on the succeeding company rows (take note that the site has pagination and a total of 22 pages (or tables) must be looped through
Kindly check my initial code for the process below. I also think that my implicitWaits are working. Does it apply on Selenium executeScript commands?
var webdriver = require('selenium-webdriver'),
By = webdriver.By,
until = webdriver.until;
var driver = new webdriver.Builder()
.forBrowser('chrome')
.build();
driver.get('http://edge.pse.com.ph/financialReports/form.do');
driver.wait(until.titleIs('Financial Reports'), 1000)
driver.manage().timeouts().implicitlyWait(5000);
driver.findElement(By.name('tmplNm')).sendKeys('Annual Report');
driver.executeScript("document.getElementsByName('fromDate').setAttribute('value', '4-23-2012')");
driver.findElement(By.id('btnSearch')).click();
var totalPages = 22;
var num = 0;
while (num < totalPages) {
var pagingArray = driver.executeScript("document.querySelector('.paging').getElementsByTagName('span')");
for (var p = 0; p < pagingArray.length; p++) {
var page = driver.executeScript("pagingArray[p].getElementsByTagName('a')[0]");
page.click();
var table = driver.executeScript("document.querySelector('table.list')");
for (var i = 0; i < table.rows.length; i++) {
for (var j = 0; j < table.rows[i].cells.length; j++) {
if (j == 1) {
// Store the current window handle
var winHandleBefore = driver.getWindowHandle();
var reportLink = driver.executeScript("table.rows[i].cells[j].getElementsByTagName('a')[0]");
reportLink.click();
// Switch to new window opened
for (var winHandle in driver.getWindowHandles()) {
driver.switchTo().window(winHandle);
}
var companyName = driver.executeScript("document.getElementById('companyName').innerHTML");
var stockTicker = driver.executeScript("document.getElementById('companyStockSymbol').innerHTML");
checkCompany(companyName, stockTicker);
var tableBS = driver.executeScript("document.getElementById('BS')");
var currentYear = driver.executeScript("tableBS.rows[1].cells[1].getElementsByTagName('span')[0]");
if (!checkBalanceSheets(companyName, currentYear)) {
bsCurrentYear = new Object();
bsCurrentYear.year = currentYear;
bsCurrentYear.currentAssets = driver.executeScript("tableBS.rows[2].cells[1].getElementsByTagName('span')[0]");
bsCurrentYear.totalAssets = driver.executeScript("tableBS.rows[3].cells[1].getElementsByTagName('span')[0]");
bsCurrentYear.currentLiabilities = driver.executeScript("tableBS.rows[4].cells[1].getElementsByTagName('span')[0]");
bsCurrentYear.totalLiabilities = driver.executeScript("tableBS.rows[5].cells[1].getElementsByTagName('span')[0]");
bsCurrentYear.company = companyName;
balanceSheets.push(bsCurrentYear);
}
var previousYear = driver.executeScript("tableBS.rows[1].cells[2].getElementsByTagName('span')[0]");
if (!checkBalanceSheets(companyName, previousYear)) {
bsPreviousYear = new Object();
bsPreviousYear.year = previousYear;
bsPreviousYear.currentAssets = driver.executeScript("tableBS.rows[2].cells[2].getElementsByTagName('span')[0]");
bsPreviousYear.totalAssets = driver.executeScript("tableBS.rows[3].cells[2].getElementsByTagName('span')[0]");
bsPreviousYear.currentLiabilities = driver.executeScript("tableBS.rows[4].cells[2].getElementsByTagName('span')[0]");
bsPreviousYear.totalLiabilities = driver.executeScript("tableBS.rows[5].cells[2].getElementsByTagName('span')[0]");
bsPreviousYear.company = companyName;
balanceSheets.push(bsPreviousYear);
}
var tableIS = driver.executeScript("document.getElementById('IS')");
if (!checkIncomeStatements(companyName, currentYear)) {
isCurrentYear = new Object();
isCurrentYear.year = currentYear;
isCurrentYear.grossrevenue = driver.executeScript("tableIS.rows[4].cells[1].getElementsByTagName('span')[0]");
isCurrentYear.grossexpense = driver.executeScript("tableIS.rows[7].cells[1].getElementsByTagName('span')[0]");
isCurrentYear.netincomeaftertax = driver.executeScript("tableIS.rows[10].cells[1].getElementsByTagName('span')[0]");
isCurrentYear.company = companyName;
isCurrentYear.eps = driver.executeScript("tableIS.rows[12].cells[1].getElementsByTagName('span')[0]");
incomeStatements.push(isCurrentYear);
}
if (!checkIncomeStatements(companyName, previousYear)) {
isPreviousYear = new Object();
isPreviousYear = previousYear;
isPreviousYear.grossrevenue = driver.executeScript("tableIS.rows[4].cells[2].getElementsByTagName('span')[0]");
isPreviousYear.grossexpense = driver.executeScript("tableIS.rows[7].cells[2].getElementsByTagName('span')[0]");
isPreviousYear.netincomeaftertax = driver.executeScript("tableIS.rows[10].cells[2].getElementsByTagName('span')[0]");
isPreviousYear.company = companyName;
isPreviousYear.eps = driver.executeScript("tableIS.rows[12].cells[2].getElementsByTagName('span')[0]");
incomeStatements.push(isPreviousYear);
}
// Close the new window, if that window no more required
driver.close();
// Switch back to original browser (first window)
driver.switchTo().window(winHandleBefore);
}
}
}
}
driver.executeScript("document.querySelector('.paging').getElementsByTagName('a')[2].click()");
num++;
}
PS CheckBalanceSheets and CheckIncomeStatements are for validating IF the year already exists and has been recorded before for the respective company
function checkCompany(name, ticker) {
var found = companies.some(function(el) {
return el.name === name;
});
if (!found) { companies.push({ name: name, ticker: ticker }); }
}
function checkBalanceSheets(name, year) {
var found = balanceSheets.some(function(el) {
return el.name === name && el.year === year;
});
}
function checkIncomeStatements(name, year) {
var found = incomeStatements.some(function(el) {
return el.name === name && el.year === year;
});
}
I have found similar threads about this but I cant seem to make their solutions work for my specific issue. I currently have a calendar that will highlight the starting date of an Event. I need to change this to highlight the Start Date through the End Date.
Note: I did not write this code. It seems like whoever wrote this left a lot of junk in here. Please don't judge.
attachTocalendar : function(json, m, y) {
var arr = new Array();
if (json == undefined || !json.month || !json.year) {
return;
}
m = json.month;
y = json.year;
if (json.events == null) {
return;
}
if (json.total == 0) {
return;
}
var edvs = {};
var kds = new Array();
var offset = en4.ynevent.getDateOffset(m, y);
var tds = $$('.ynevent-cal-day');
var selected = new Array(), numberOfEvent = new Array();
for (var i = 0; i < json.total; ++i) {
var evt = json.events[i];
var s1 = evt.starttime.toTimestamp();
var s0 = s1;
var s2 = evt.endtime.toTimestamp();
var ds = new Date(s1);
var de = new Date(s2);
var id = ds.getDateCellId();
index = selected.indexOf(id);
if (index < 0)
{
numberOfEvent[selected.length] = 1;
selected.push(id);
}
else
{
numberOfEvent[index] = numberOfEvent[index] + 1;
}
}
for (var i = 0; i < selected.length; i++) {
var td = $(selected[i]);
if (td != null) {
if (!(td.hasClass("otherMonth"))){
td.set('title', numberOfEvent[i] + ' ' + en4.core.language.translate(numberOfEvent[i] > 1 ? 'events' : 'event'));
td.addClass('selected');
}
}
}
},
Instead of trying to select them all, I recommend you iterate over them instead. So something like this:
function highlightDates(startDate, endDate) {
var curDate = startDate;
var element;
$('.selected').removeClass('selected'); // reset selection
while (curDate <= endDate) {
element = getElementForDate(curDate)
element.addClass('selected');
curDate.setDate(curDate.getDate() + 1); // add one day
}
}
function getElementForDate(someDate) {
return $('#day-' + someDate.getYear() + "-" + someDate.getMonth() + "-" + someDate.getDay());
}
I'm working on a little Google Apps Script that will export event data from a selected Google Calendar to a new Google Spreadsheet for a selected date range. One problem I'm having is that the times when copied over to the spreadsheet are off by three hours. Any suggestions as to how to handle this and display the event times correctly?
Here's my code so far: (It's a work-in-progress)
function doGet() {
var app = UiApp.createApplication();
var handler = app.createServerHandler("change");
var picker1 = app.createDatePicker().addValueChangeHandler(handler).setId("picker1");
var picker2 = app.createDatePicker().addValueChangeHandler(handler).setId("picker2");
var pickerpanel = app.createHorizontalPanel();
var panel = app.createVerticalPanel();
pickerpanel.add(picker1);
pickerpanel.add(picker2);
panel.add(pickerpanel);
var lb = app.createListBox(false).setId('lbCalSelId').setName('lbCalSelect');
lb.setVisibleItemCount(3);
var cals = CalendarApp.getAllCalendars();
for (var i=0; i<cals.length;i++) {
lb.addItem(cals[i].getName(),cals[i].getId());
}
panel.add(lb);
var button = app.createPushButton().setText("Export").setId("button");
var handler = app.createServerClickHandler('doExport').addCallbackElement(panel);
button.addClickHandler(handler);
panel.add(button);
app.add(panel);
return app;
}
function change(eventInfo) {
var app = UiApp.getActiveApplication();
if (eventInfo.parameter.picker1) {
UserProperties.setProperties({"DateRangeStart":eventInfo.parameter.picker1});
app.add(app.createLabel("Start date " + eventInfo.parameter.picker1));
}
else if (eventInfo.parameter.picker2) {
UserProperties.setProperties({"DateRangeEnd":eventInfo.parameter.picker2});
app.add(app.createLabel("End date" + eventInfo.parameter.picker2));
}
return app;
}
function doExport(eventInfo) {
var app = UiApp.getActiveApplication();
var calId = eventInfo.parameter.lbCalSelect;
var cal = CalendarApp.getCalendarById(calId);
var rangeStart = UserProperties.getProperty("DateRangeStart");
var rangeEnd = UserProperties.getProperty("DateRangeEnd");
app.add(app.createLabel("The button was clicked!"));
if (rangeStart && rangeEnd) {
app.add(app.createLabel("exporting..."));
var events = cal.getEvents(new Date(rangeStart), new Date(rangeEnd));
var eventsData = [];
var headerRow = ['Title','Start Time','End Time','Location','Description'];
for (var i=0; i < events.length; i++) {
var eventData = [];
eventData.push(events[i].getTitle());
eventData.push(events[i].getStartTime());
eventData.push(events[i].getEndTime());
eventData.push(events[i].getLocation());
eventData.push(events[i].getDescription());
eventsData.push(eventData);
}
var ss = SpreadsheetApp.create("Export of " + cal.getName() + " from " + rangeStart + " to " + rangeEnd);
var sheet = ss.getSheets()[0];
var destRange = sheet.getRange(1, 1, events.length, headerRow.length);
destRange.setValues(eventsData);
}
else {
app.add(app.createLabel("Range not specified."));
}
return app;
}
I figured it out.
I just set the timezone of the spreadsheet to the same timezone as the calendar and it worked like a charm.
ss.setSpreadsheetTimeZone(cal.getTimeZone());